Setup

Set random seed

This ensures that all results are replicable

set.seed(5241988)

Load Packages

Define a function that will take a list of required packages, install any that are missing, and add all packages to the local workspace:

get_packages<-function(package_list){
  # Install packages not yet installed
  installed_packages <- package_list %in% rownames(installed.packages())
  if (any(installed_packages == FALSE)) {
    install.packages(package_list[!installed_packages])
  }
  # Packages loading
  invisible(lapply(package_list, require, character.only = TRUE))}

Apply to the package list for this analysis:

packages<-c('dplyr','tidyr','magrittr','ggplot2','statcomp','bayesplot','rstanarm','bayestestR','BayesFactor','irr','gridExtra','parallel','shinystan')
get_packages(packages)

Set some environmental variables:

options(mc.cores=detectCores())
test_regex="^scale.*|^\\(Int.*"
regex="^\\(Int|^Age.*|^Sex.*|^Birth.*|^s\\(B.*"
my_probs=c(0.0275,0.5,0.975)

Set up Workspace

Set Session Variables

Set color scheme and line types for tokens

voiced_col<-RColorBrewer::brewer.pal(4, "Paired")[1]
voiced_col_alt<-RColorBrewer::brewer.pal(4, "Paired")[2]
voiceless_col<-RColorBrewer::brewer.pal(4, "Paired")[3]
voiceless_col_alt<-RColorBrewer::brewer.pal(4, "Paired")[4]
male_col<-RColorBrewer::brewer.pal(4, "Accent")[2]
female_col<-RColorBrewer::brewer.pal(4, "Accent")[3]
female_line<-'solid'
male_line<-'dashed'

Set Plot Theme

Set general theme for plots

theme_set(theme_minimal()+
            theme(plot.title = element_text(size=22),
                  legend.key.size = unit(1,"cm"),
                  legend.text = element_text(size=12),
                  legend.title = element_text(size=15),
                  panel.spacing=unit(2,"lines"),
                  panel.background=element_rect(color='black'),
                  strip.text=element_text(color='black',size=18),
                  axis.text=element_text(color='black',size=12),
                  axis.title=element_text(color='black',size=15)))

Pre-load models

By default, the chunks in this notebook that pertain to the statistical analysis have been set not to evaluate. This will load all models to the workspace so they can be queried without having to wait for them to run.

load('../Data/all_models_final.RData')

Test Set Data

By default, we’ll assume that the script hasn’t been moved from its original location and that all data files are in the same directory as this script. If that’s not true, use setwd() to change the working directory first.

Data Processing

Load in test set and process

test_set=read.delim('../Data/testset_results.txt')
process_test_set=function(data){
  data$Speaker=with(data,factor(ifelse(substr(File,1,1)=='A',"A","G")))
  data$File=as.factor(data$File)
  data$Phone=as.factor(data$Phone)
  data$Word=as.factor(data$Word)
  data$Mean_HNR=as.numeric(data$Mean_HNR)
  return(data)}
test_set %<>% process_test_set()

Divide into voiceless and voiced

voiced_test_set<-test_set %>% filter(.,Phone=='DH'|Phone=='D') %>% droplevels()
voiceless_test_set<-test_set %>% filter(.,Phone=="TH"|Phone=="T") %>% droplevels()
voiced_test_set$Phone<-relevel(voiced_test_set$Phone,ref="DH")
voiceless_test_set$Phone<-relevel(voiceless_test_set$Phone,ref="TH")

Data Visualization

phones<-rev(c('/θ/','/t/','/ð/','/d/'))
cog<-ggplot(test_set,aes(y=Phone,x=CoG,fill=Phone))+geom_violin()+stat_summary(geom='errorbar',width=0.25,fun.data='mean_cl_boot')+scale_fill_brewer(palette='Paired')+scale_y_discrete(labels=phones)+coord_cartesian(xlim=c(0,1500))+theme(legend.position='none')+labs(x="Center of Gravity")
skew<-ggplot(test_set,aes(x=Skewness,y=Phone,fill=Phone))+geom_violin()+stat_summary(geom='errorbar',width=0.25,fun.data='mean_cl_boot')+scale_fill_brewer(palette='Paired')+scale_y_discrete(labels=phones)+coord_cartesian(xlim=c(0,25))+theme(legend.position='none')
kur<-ggplot(test_set,aes(x=Kurtosis,y=Phone,fill=Phone))+geom_violin()+stat_summary(geom='errorbar',width=0.25,fun.data='mean_cl_boot')+scale_fill_brewer(palette='Paired')+scale_y_discrete(labels=phones)+coord_cartesian(xlim=c(0,500))+theme(legend.position='none')
hnr<-ggplot(test_set,aes(x=Mean_HNR,y=Phone,fill=Phone))+geom_violin()+stat_summary(geom='errorbar',width=0.25,fun.data='mean_cl_boot')+scale_fill_brewer(palette='Paired')+scale_y_discrete(labels=phones)+theme(legend.position='none')+labs(x="Mean HNR")
grid.arrange(cog,hnr,skew,kur)->figure1

Data Analysis

Run models

Voiceless Data

ms_testset=stan_glmer(Phone~scale(Mean_HNR)+scale(CoG)+scale(Skewness)+scale(Kurtosis)+(1|Speaker),voiceless_test_set,family='binomial',adapt_delta=0.99,warmup=2000,iter=6000,chains=8)
ms_testset_loo<-loo(ms_testset,k_threshold=0.7)

Voiced Data

mv_testset=stan_glmer(Phone~scale(Mean_HNR)+scale(CoG)+scale(Skewness)+scale(Kurtosis)+(1|Speaker),voiced_test_set,family='binomial',adapt_delta=0.99,warmup=2000,iter=6000,chains=8)
mv_testset_loo<-loo(mv_testset,k_threshold=0.7)

View Results

Model Summary

summary(ms_testset,regex_pars=test_regex,probs=my_probs,digits=3)

Model Info:
 function:     stan_glmer
 family:       binomial [logit]
 formula:      Phone ~ scale(Mean_HNR) + scale(CoG) + scale(Skewness) + scale(Kurtosis) + 
       (1 | Speaker)
 algorithm:    sampling
 sample:       32000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 334
 groups:       Speaker (2)

Estimates:
                  mean   sd     2.75%   50%    97.5%
(Intercept)      0.510  0.507 -0.545   0.529  1.508 
scale(Mean_HNR) -0.035  0.173 -0.369  -0.036  0.307 
scale(CoG)      -0.821  0.203 -1.221  -0.815 -0.439 
scale(Skewness) -1.888  0.391 -2.653  -1.883 -1.138 
scale(Kurtosis)  0.939  0.343  0.282   0.939  1.619 

MCMC diagnostics
                mcse  Rhat  n_eff
(Intercept)     0.012 1.003  1861
scale(Mean_HNR) 0.001 1.000 26973
scale(CoG)      0.002 1.000 18019
scale(Skewness) 0.003 1.000 15284
scale(Kurtosis) 0.003 1.000 16051

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
summary(mv_testset,regex_pars=test_regex,probs=my_probs,digits=3)

Model Info:
 function:     stan_glmer
 family:       binomial [logit]
 formula:      Phone ~ scale(Mean_HNR) + scale(CoG) + scale(Skewness) + scale(Kurtosis) + 
       (1 | Speaker)
 algorithm:    sampling
 sample:       32000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 264
 groups:       Speaker (2)

Estimates:
                  mean   sd     2.75%   50%    97.5%
(Intercept)      0.664  0.870 -1.172   0.690  2.419 
scale(Mean_HNR) -1.133  0.285 -1.697  -1.129 -0.582 
scale(CoG)      -0.273  0.244 -0.746  -0.271  0.203 
scale(Skewness)  1.406  0.482  0.504   1.399  2.370 
scale(Kurtosis) -1.436  0.431 -2.286  -1.429 -0.613 

MCMC diagnostics
                mcse  Rhat  n_eff
(Intercept)     0.008 1.000 10948
scale(Mean_HNR) 0.002 1.000 23745
scale(CoG)      0.002 1.000 18239
scale(Skewness) 0.004 1.000 15339
scale(Kurtosis) 0.003 1.000 16371

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).

HDIs and Bayes Factors

print("Voiceless Data:")
[1] "Voiceless Data:"
hdi(ms_testset)
Highest Density Interval 

Parameter       |        95% HDI
--------------------------------
(Intercept)     | [-0.55,  1.55]
scale(Mean_HNR) | [-0.38,  0.30]
scale(CoG)      | [-1.22, -0.43]
scale(Skewness) | [-2.65, -1.12]
scale(Kurtosis) | [ 0.27,  1.62]
bayesfactor(ms_testset)
Sampling priors, please wait...
Warning: Bayes factors might not be precise.
  For precise Bayes factors, sampling at least 40,000 posterior samples is recommended.
Bayes Factor (Savage-Dickey density ratio) 

Parameter       |       BF
--------------------------
(Intercept)     |    0.770
scale(Mean_HNR) |    0.070
scale(CoG)      |   429.50
scale(Skewness) | 3.46e+03
scale(Kurtosis) |     6.16

* Evidence Against The Null: 0
print("Voiced Data:")
[1] "Voiced Data:"
hdi(mv_testset)
Highest Density Interval 

Parameter       |        95% HDI
--------------------------------
(Intercept)     | [-1.10,  2.54]
scale(Mean_HNR) | [-1.69, -0.56]
scale(CoG)      | [-0.76,  0.20]
scale(Skewness) | [ 0.50,  2.38]
scale(Kurtosis) | [-2.29, -0.60]
bayesfactor(mv_testset)
Sampling priors, please wait...
Warning: Bayes factors might not be precise.
  For precise Bayes factors, sampling at least 40,000 posterior samples is recommended.
Bayes Factor (Savage-Dickey density ratio) 

Parameter       |     BF
------------------------
(Intercept)     |  0.567
scale(Mean_HNR) | 273.53
scale(CoG)      |  0.183
scale(Skewness) |  17.11
scale(Kurtosis) |  61.60

* Evidence Against The Null: 0

Posterior Draws

color_scheme_set(c(voiceless_col,rep("#000000",5)))
ms_testset_post<-mcmc_areas(ms_testset,regex_pars=test_regex,prob_outer=0.95)+ggtitle("Voiceless")+scale_y_discrete(labels=c("Intercept","Mean HNR","CoG","Skewness","Kurtosis"))+geom_vline(xintercept=0,linetype='dotted')+labs(x="Stop Likelihood")
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
color_scheme_set(c(voiced_col,rep("#000000",5)))
mv_testset_post<-mcmc_areas(mv_testset,regex_pars=test_regex,prob_outer=0.95)+ggtitle("Voiced")+scale_y_discrete(labels=c("Intercept","Mean HNR","CoG","Skewness","Kurtosis"))+geom_vline(xintercept=0,linetype='dotted')+labs(x="Stop Likelihood")
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
grid.arrange(ms_testset_post,mv_testset_post,ncol=2)->figure2

Region of Practical Equivalence (ROPE)

ms_testset_rope<-plot(rope(ms_testset,range=rope_range(ms_testset),ci_method='hdi'),alpha=0.75)+ggtitle("Voiceless")+scale_fill_manual(values=c(voiceless_col,voiceless_col_alt))
Possible multicollinearity between scale(Kurtosis) and scale(Skewness) (r = 0.87). This might lead to inappropriate results. See 'Details' in '?rope'.
mv_testset_rope<-plot(rope(mv_testset,range=rope_range(mv_testset),ci_method='hdi'),alpha=0.75)+ggtitle("Voiced")+scale_fill_manual(values=c(voiced_col,voiced_col_alt))
Possible multicollinearity between scale(Kurtosis) and scale(Skewness) (r = 0.88). This might lead to inappropriate results. See 'Details' in '?rope'.
grid.arrange(ms_testset_rope,mv_testset_rope,ncol=1)

Shinystan App (will open in new browser if code is run)

launch_shinystan(ms_testset)
launch_shinystan(mv_testset)

PREP Data

PREP = Puerto Rican English in Philadelphia Corpus

More info

Data Processing

Load Data from Sources

By default, we’ll assume that the script hasn’t been moved from its original location and that all data files are in the same directory as this script. If that’s not true, use setwd() to change the working directory first.

prep_data=read.delim('../Data/prep_data.txt')
socio_data=read.csv('../Data/prep_socio.csv')
perceptual_data=read.delim('../Data/perceptual_data.txt')
perceptual_tokens_voicing_codes=read.delim('../Data/voicing_codes.txt')

Process Data

Start by getting phonetic context

prep_data$FollowingStress=as.factor(with(prep_data,substr(FollowingPhone,nchar(FollowingPhone),nchar(FollowingPhone))))
prep_data$FollowingVowel=as.factor(with(prep_data,substr(FollowingPhone,1,nchar(FollowingPhone)-1)))
prep_data$FollowingPhone=as.factor(prep_data$FollowingPhone)

Remove tokens that aren’t prevocalic (i.e., are consonant clusters)

prep_data %<>% filter(!(FollowingPhone %in% list('R','W','Y'))) 
prep_data %<>% mutate(Voicing=if_else(Phone %in% list('D','DH'),'Voiced','Voiceless'),Manner=if_else(Phone %in% list('D','T'),'Stop','Fricative'),Metric=if_else(Voicing=='Voiced','Mean_HNR','Skewness'))

Join to sociodemographic data and specify data structure

prep_data %>% left_join(.,socio_data, by=c('File'='Speaker'))->prep_socio # Merge socio data with speaker data
prep_socio$Sex=as.factor(prep_socio$Sex)
prep_socio$Mean_HNR=as.numeric(prep_socio$Mean_HNR)
prep_socio$File=as.factor(prep_socio$File)
prep_socio$Word=gsub("\\(\\(","",prep_socio$Word)
prep_socio$Word=gsub("\\)\\)","",prep_socio$Word)
prep_socio$Word=as.factor(prep_socio$Word)
prep_socio$Phone=as.factor(prep_socio$Phone)
prep_socio %<>% mutate(AgeGroup=if_else(BirthYear>=1985,'Younger','Older'),Speaker=File)

Get average values by speaker, voicing, and phonetic context

prep_data$PrecedingPhone=as.factor(prep_data$PrecedingPhone)
prep_data$PrecedingWord=as.factor(prep_data$PrecedingWord)
prep_data$Skewness=as.numeric(prep_data$Skewness)
prep_data$Mean_HNR=as.numeric(prep_data$Mean_HNR)

speaker_avgs<-prep_data %>% filter(Phone=='D' | Phone=='T') %>% 
  group_by(File, Phone, FollowingVowel, FollowingStress) %>% 
  reframe(count=n(),HNR_stop=mean(na.omit(Mean_HNR)),sd_HNR_stop=sd(na.omit(Mean_HNR)), skewness_stop=mean(na.omit(Skewness)),sd_skewness_stop=sd(na.omit(Skewness))) %>%
  ungroup()

voiced_avgs<-speaker_avgs %>% filter(Phone=='D') %>% select(-Phone)
voiceless_avgs<-speaker_avgs %>% filter(Phone=='T') %>% select(-Phone)

Functions to calculate normalized stopping ratios and remove outliers >3 standard deviations from the group mean, by speaker.

make_stopping_ratios<-function(data,p){
  data %<>% filter(Phone==p) %>% 
  left_join(voiced_avgs,by=c('File','FollowingVowel','FollowingStress')) %>% 
  mutate(HNR_ratio=Mean_HNR/HNR_stop,skewness_ratio=Skewness/skewness_stop) %>%
  mutate(ratio=(if_else(Phone=='TH',skewness_ratio,if_else(Phone=="DH",HNR_ratio,NA)))) %>%
  left_join(socio_data,by=c('File'='Speaker')) %>% rename(Speaker=File) %>%
  mutate(AgeGroup=as.factor(ifelse(BirthYear>=1985,'Younger','Older')))
  return(data)
}

remove_outliers<-function(data){
  data %>% filter(!is.na(ratio)) %>% mutate(Pseudonym=ifelse(.$Speaker=='S39','Carina',.$Pseudonym))%>%
    mutate(mean_ratio_byspeaker=mean(ratio),sd_ratio_byspeaker=sd(ratio)) %>%
    ungroup() %>%
    filter(abs(ratio-mean_ratio_byspeaker)<=3*sd_ratio_byspeaker) %>%
    filter(abs(ratio-mean(ratio))<=3*sd(ratio))->result
  print(paste0("Removed ",nrow(data)-nrow(result)," rows, or ",round(100*(1-nrow(result)/nrow(data)),2),"% of data."))
  return(result)
}

Apply to data and create production dataset

voiceless<-make_stopping_ratios(prep_data,"TH") %>% remove_outliers()
[1] "Removed 76 rows, or 18.01% of data."
voiced<-make_stopping_ratios(prep_data,"DH") %>% remove_outliers()
[1] "Removed 1163 rows, or 23.34% of data."
production_data<-rbind(voiced,voiceless)

Data Visualization

Data distribution

production_data %>% group_by(Word,Phone) %>% summarise(count=n()) %>% arrange(desc(count)) %>%  {.->> word_cts} %>%
  ggplot(.,aes(x=count))+geom_density()+scale_x_continuous(breaks=seq(0,500,by=50))+coord_cartesian(xlim=c(0,500))+ggtitle("Word Distribution")
`summarise()` has grouped output by 'Word'. You can override using the `.groups` argument.

Note that only 2 of the top 10 words are /θ/ words (think,things)

word_cts %>%
  filter(count>=40&(Phone=='DH'|Phone=='TH')) %>% 
  arrange(desc(count)) %>% 
  head(20) 

Top 10 /ð/ words

word_cts %>%
  filter(Phone=='DH') %>% 
  arrange(desc(count)) %>% 
  head(10) 

Top 10 /θ/ words

word_cts %>%
  filter(Phone=='TH') %>% 
  arrange(desc(count)) %>% 
  head(10) 

Visualize distribution of fricatives and stops across data by metric


ggplot(prep_socio,aes(x=if_else(Voicing=='Voiced',Mean_HNR,Skewness),fill=Manner))+
  geom_density(alpha=0.5)+facet_grid(Voicing~.)+
  labs(x="Metric",y="Density")+scale_fill_viridis_d(option='cividis',begin=0.3,end=0.9)+theme(legend.position='bottom')


ggplot(prep_socio,aes(x=if_else(Voicing=='Voiced',Mean_HNR,Skewness),fill=Manner))+
  geom_density(alpha=0.5)+facet_grid(Voicing~Sex)+
  labs(x="Metric",y="Density")+scale_fill_viridis_d(option='viridis',begin=0.2,end=0.6)+theme(legend.position='bottom')

 
ggplot(prep_socio,aes(x=if_else(Voicing=='Voiced',Mean_HNR,Skewness),fill=Manner))+
  geom_density(alpha=0.5)+facet_grid(Sex~AgeGroup+Voicing)+
  labs(x="Metric",y="Density")+scale_fill_viridis_d(option='inferno')+theme(legend.position='bottom')

Individual ranges to see if there are any major outliers that remain after trimming procedure

ggplot(voiceless,aes(x=Pseudonym,y=ratio))+
  geom_boxplot(fill=voiceless_col)+
  labs(x="Speaker",y="Normalized Stopping Ratio\n(Skewness)")+
  ggtitle("Voiceless")+scale_y_continuous(breaks=seq(-1,5,by=0.5))+geom_hline(yintercept=1,linetype='dashed')+
  coord_flip()->p_voiceless_byspeaker
ggplot(voiced,aes(x=Pseudonym,y=ratio))+
  geom_boxplot(fill=voiced_col)+
  labs(x="Speaker",y="Normalized Stopping Ratio\n(Mean HNR)")+
  scale_y_continuous(breaks=seq(-1,5,by=1))+
  ggtitle("Voiced")+geom_hline(yintercept=1,linetype='dashed')+
  coord_flip()->p_voiced_byspeaker
grid.arrange(p_voiceless_byspeaker,p_voiced_byspeaker,ncol=2)

Data Analysis

Set Contrasts

set_contrasts<-function(data){
  data$Sex<-as.factor(data$Sex)
  data$Sex<-relevel(data$Sex,ref='Male')
  data$AgeGroup=as.factor(data$AgeGroup)
  data$AgeGroup<-relevel(data$AgeGroup,ref="Older")
  return(data)
  }
voiced %<>% set_contrasts()
voiceless %<>% set_contrasts()
voiced$HNR_ratio_deviance=voiced$HNR_ratio-1
voiceless$skewness_ratio_deviance=voiceless$skewness_ratio-1

Run Models

Voiceless Data

ms_prep_gamm<-stan_gamm4(skewness_ratio_deviance~Sex*BirthYear+s(BirthYear,bs='tp',by=Sex,k=3,m=2),random=~(1|Speaker),data=voiceless,adapt_delta=0.999,warmup=2000,iter=6000,chains=8)
ms_prep_gamm_loo<-loo(ms_prep_gamm,k_threshold=0.7)

Voiced Data

mv_prep_gamm<-stan_gamm4(HNR_ratio_deviance~Sex*BirthYear+s(BirthYear,bs='tp',by=Sex,k=3,m=2),random=~(1|Speaker),data=voiced,adapt_delta=0.999,warmup=2000,iter=6000,chains=8)
mv_prep_gamm_loo<-loo(mv_prep_gamm,k_threshold=0.7)

View Results

Model Summary

Voiceless
summary(ms_prep_gamm,regex_pars=regex,probs=my_probs,digits=3)

Model Info:
 function:     stan_gamm4
 family:       gaussian [identity]
 formula:      skewness_ratio_deviance ~ Sex * BirthYear + s(BirthYear, bs = "tp", 
       by = Sex, k = 3, m = 2)
 algorithm:    sampling
 sample:       32000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 346
 groups:       Speaker (32)

Estimates:
                           mean    sd      2.75%   50%     97.5%
(Intercept)               10.825  28.095 -43.075   9.385  68.913
SexFemale                 -0.093   1.775  -3.491  -0.106   3.409
BirthYear                 -0.005   0.014  -0.034  -0.005   0.023
SexFemale:BirthYear        0.000   0.001  -0.002   0.000   0.002
s(BirthYear):SexMale.1    -0.023   0.231  -0.532  -0.006   0.471
s(BirthYear):SexMale.2    -0.072   0.187  -0.465  -0.046   0.288
s(BirthYear):SexFemale.1  -0.208   0.208  -0.655  -0.179   0.107
s(BirthYear):SexFemale.2   0.063   0.185  -0.291   0.040   0.460

MCMC diagnostics
                         mcse  Rhat  n_eff
(Intercept)              0.280 1.001 10047
SexFemale                0.012 1.000 20838
BirthYear                0.000 1.001 10046
SexFemale:BirthYear      0.000 1.000 20858
s(BirthYear):SexMale.1   0.002 1.000 20402
s(BirthYear):SexMale.2   0.002 1.001 10350
s(BirthYear):SexFemale.1 0.002 1.001 10682
s(BirthYear):SexFemale.2 0.002 1.001 10220

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
Voiced
summary(mv_prep_gamm,regex_pars=regex,probs=my_probs,digits=3)

Model Info:
 function:     stan_gamm4
 family:       gaussian [identity]
 formula:      HNR_ratio_deviance ~ Sex * BirthYear + s(BirthYear, bs = "tp", 
       by = Sex, k = 3, m = 2)
 algorithm:    sampling
 sample:       32000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 3819
 groups:       Speaker (32)

Estimates:
                           mean    sd      2.75%   50%     97.5%
(Intercept)               -6.346  28.280 -64.967  -5.238  53.315
SexFemale                  0.047   1.970  -3.695   0.032   3.921
BirthYear                  0.003   0.014  -0.026   0.003   0.034
SexFemale:BirthYear        0.000   0.001  -0.002   0.000   0.002
s(BirthYear):SexMale.1     0.161   0.237  -0.209   0.110   0.717
s(BirthYear):SexMale.2     0.057   0.194  -0.330   0.038   0.475
s(BirthYear):SexFemale.1   0.094   0.134  -0.123   0.070   0.399
s(BirthYear):SexFemale.2  -0.057   0.195  -0.465  -0.039   0.352

MCMC diagnostics
                         mcse  Rhat  n_eff
(Intercept)              0.282 1.000 10059
SexFemale                0.013 1.000 21484
BirthYear                0.000 1.000 10058
SexFemale:BirthYear      0.000 1.000 21443
s(BirthYear):SexMale.1   0.002 1.000 12413
s(BirthYear):SexMale.2   0.002 1.000 10323
s(BirthYear):SexFemale.1 0.001 1.001  8250
s(BirthYear):SexFemale.2 0.002 1.001 10171

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
HDIs and Bayes Factors
Voiceless
hdi(ms_prep_gamm)
Highest Density Interval 

Parameter           |         95% HDI
-------------------------------------
(Intercept)         | [-45.07, 68.84]
SexFemale           | [ -3.53,  3.45]
BirthYear           | [ -0.03,  0.02]
SexFemale:BirthYear | [ -0.00,  0.00]

# Fixed effects smooth_sd

Parameter               | Function |         95% HDI
----------------------------------------------------
s(BirthYear):SexMale1   |   smooth | [  0.00,  0.92]
s(BirthYear):SexMale2   |   smooth | [  0.00,  0.86]
s(BirthYear):SexFemale1 |   smooth | [  0.00,  1.01]
s(BirthYear):SexFemale2 |   smooth | [  0.00,  0.85]
bayesfactor(ms_prep_gamm)
Sampling priors, please wait...
Warning: Bayes factors might not be precise.
  For precise Bayes factors, sampling at least 40,000 posterior samples is recommended.
Bayes Factor (Savage-Dickey density ratio) 

Parameter           |    BF
---------------------------
(Intercept)         | 0.119
SexFemale           | 0.707
BirthYear           | 0.119
SexFemale:BirthYear | 0.692

* Evidence Against The Null: 0
Voiced
hdi(mv_prep_gamm)
Highest Density Interval 

Parameter           |         95% HDI
-------------------------------------
(Intercept)         | [-66.83, 53.56]
SexFemale           | [ -3.70,  4.00]
BirthYear           | [ -0.03,  0.03]
SexFemale:BirthYear | [ -0.00,  0.00]

# Fixed effects smooth_sd

Parameter               | Function |         95% HDI
----------------------------------------------------
s(BirthYear):SexMale1   |   smooth | [  0.00,  1.09]
s(BirthYear):SexMale2   |   smooth | [  0.00,  0.96]
s(BirthYear):SexFemale1 |   smooth | [  0.00,  0.93]
s(BirthYear):SexFemale2 |   smooth | [  0.00,  0.97]
bayesfactor(mv_prep_gamm)
Sampling priors, please wait...
Warning: Bayes factors might not be precise.
  For precise Bayes factors, sampling at least 40,000 posterior samples is recommended.
Bayes Factor (Savage-Dickey density ratio) 

Parameter           |    BF
---------------------------
(Intercept)         | 0.094
SexFemale           | 0.702
BirthYear           | 0.094
SexFemale:BirthYear | 0.694

* Evidence Against The Null: 0
Posterior Draws
color_scheme_set(c(voiceless_col,rep("#000000",5)))
ms_prep_gamm_post<-mcmc_areas(ms_prep_gamm,regex_pars=regex,prob_outer=0.95)+ggtitle("Voiceless")
color_scheme_set(c(voiced_col,rep("#000000",5)))
mv_prep_gamm_post<-mcmc_areas(mv_prep_gamm,regex_pars=regex,prob_outer=0.95)+ggtitle("Voiced")
grid.arrange(ms_prep_gamm_post,mv_prep_gamm_post,ncol=2)

Nonlinear Plot

color_scheme_set(c(voiceless_col,rep("#000000",5)))
ms_prep_gamm_nl<-plot_nonlinear(ms_prep_gamm,prob=0.95)+ggtitle("Voiceless Data")
color_scheme_set(c(voiced_col,rep("#000000",5)))
mv_prep_gamm_nl<-plot_nonlinear(mv_prep_gamm,prob=0.95)+ggtitle("Voiced Data")
grid.arrange(ms_prep_gamm_nl,mv_prep_gamm_nl,ncol=1)

Region of Practical Equivalence (ROPE)
Voiceless
ms_prep_gamm_rope<-plot(rope(ms_prep_gamm,range=rope_range(ms_prep_gamm),ci_method='hdi'))+ggtitle("Voiceless")+scale_fill_manual(values=c(voiceless_col,voiceless_col_alt))+theme(legend.position='bottom')
ms_prep_gamm_rope

Voiced
mv_prep_gamm_rope<-plot(rope(mv_prep_gamm,range=rope_range(mv_prep_gamm),ci_method='hdi'))+ggtitle("Voiced")+scale_fill_manual(values=c(voiced_col,voiced_col_alt))+theme(legend.position='bottom')

mv_prep_gamm_rope

Shinystan Apps (will open in new browser if code is run)

Voiceless Data

launch_shinystan(ms_prep_gamm)

Voiced Data

launch_shinystan(mv_prep_gamm)

Perceptual Coding

Data Processing

perceptual_data %>% filter(Coder!="") %>%
  mutate(token=paste(Speaker,Word,Start,End,sep="_")) %>% 
  left_join(.,perceptual_tokens_voicing_codes,by='Word') %>% 
  left_join(.,socio_data,by='Speaker') %>% 
  mutate(AgeGroup=ifelse(as.numeric(BirthYear)>=1985,'Younger','Older')) %>%
  filter(Code!="Unsure")->perceptual_dataset_raw
perceptual_dataset_raw %<>% set_contrasts()
perceptual_dataset_raw %>% 
  group_by(Speaker,AgeGroup,BirthYear,Sex,Voicing,Code) %>% 
  tally() %>% pivot_wider(names_from=Code,values_from=n) %>% 
  mutate(Total=Stop+Fricative,Stopping_Rate=100*Stop/(Total)) %>% 
  {.->> perceptual_rates_raw} %>% print()

Labov (dh) Index Comparison

perceptual_data %>% filter(Coder!="") %>%
  mutate(token=paste(Speaker,Word,Start,End,sep="_")) %>% 
  left_join(.,perceptual_tokens_voicing_codes,by='Word') %>% 
  left_join(.,socio_data,by='Speaker') %>% 
  mutate(AgeGroup=ifelse(as.numeric(BirthYear)>=1985,'Younger','Older'),dh_value=case_match(Code,"Unsure"~1,"Fricative"~0,"Stop"~2)) %>% {.->>perceptual_group_level} %>%
  group_by(AgeGroup,Sex) %>% reframe(dh_index=100*mean(dh_value))

perceptual_group_level %>% 
  group_by(Voicing,AgeGroup,Sex,Code) %>% 
  filter(Code!="Unsure") %>%
  tally() %>% group_by(AgeGroup,Sex,Voicing) %>% 
  pivot_wider(names_from=Code,values_from=n) %>% 
  mutate(Total=Stop+Fricative,Stopping_Rate=100*Stop/(Total))

perceptual_group_level %>% filter(Coder!="") %>% group_by(Sex,AgeGroup) %>% reframe(count=n_distinct(Speaker))

Rater Consensus

Get Fleiss’ Kappa for IRR

perceptual_data %>% filter(Coder!="") %>% 
  mutate(token=paste(Speaker,Word,Start,End,sep="_")) %>% 
  select(token,Coder,Code) %>% 
  pivot_wider(names_from=`Coder`,values_from=Code) %>% {. ->> perceptual_irr} %>%
  kappam.fleiss()
 Fleiss' Kappa for m Raters

 Subjects = 1020 
   Raters = 23 
    Kappa = 0.102 

        z = 75.9 
  p-value = 0 

Show tokens by level of agreement

perceptual_irr %>% rowwise %>%
        mutate(distinct = n_distinct(unlist(across(starts_with("A"))))) %>%
        ungroup() -> perceptual_irr_agreement

perceptual_irr_agreement

Data Visualization

Stopping Rates by Sex and Birth Year based on Individual Rater Score

perceptual_voiceless_rates_raw<- perceptual_rates_raw %>%
  filter(Voicing=="Voiceless") %>%
  ggplot(aes(x=BirthYear,y=Stopping_Rate,linetype=Sex))+
  geom_rug(sides="br",linetype='solid')+
  geom_smooth(aes(group=Sex),color='black',fill=voiceless_col,method='gam',formula=y~s(x,k=3,m=2))+
  #scale_y_continuous(breaks=seq(-1.5,1.5,by=0.5))+
  geom_point(alpha=0.5,position=position_jitter(0.3))+
  labs(x="Birth Year",y="Stopping Rate\n(Perceptual, by Rater)")+
  ggtitle("Voiceless")+scale_linetype_manual(values=c(male_line,female_line))+
  theme(legend.position='bottom',strip.text=element_text(size=15))+scale_x_continuous(expand = expansion(mult = 0.1))

perceptual_voiced_rates_raw<- perceptual_rates_raw %>%
  filter(Voicing=="Voiced") %>%
  ggplot(aes(x=BirthYear,y=Stopping_Rate,linetype=Sex))+
  geom_rug(sides="br",linetype='solid')+
  geom_smooth(aes(group=Sex),color='black',fill=voiced_col,method='gam',formula=y~s(x,k=3,m=2))+
  geom_point(alpha=0.5,position=position_jitter(0.3,0.3))+
  labs(x="Birth Year",y="Stopping Rate\n(Perceptual, by Rater)")+
  ggtitle("Voiced")+scale_linetype_manual(values=c(male_line,female_line))+
  theme(legend.position='bottom',strip.text=element_text(size=15))+scale_x_continuous(expand = expansion(mult = 0.1))

grid.arrange(perceptual_voiceless_rates_raw,perceptual_voiced_rates_raw,ncol=2)

Data Analysis

Run Models

Force outcome variable to be factor for binomial gam

perceptual_dataset_raw$Code %<>% as.factor()

Voiceless Data

ms_per_raw_gamm<-stan_gamm4(Code~BirthYear*Sex+s(BirthYear,by=Sex,k=3,m=2,bs='tp'),random=~(1|Speaker),data=subset(perceptual_dataset_raw,Voicing=="Voiceless"),family='binomial',adapt_delta=0.999,warmup=2000,iter=6000,chains=8)

ms_per_raw_gamm_loo<-loo(ms_per_raw_gamm)

Voiced Data

mv_per_raw_gamm<-stan_gamm4(Code~BirthYear*Sex+s(BirthYear,by=Sex,k=3,m=2,bs='tp'),random=~(1|Speaker),data=subset(perceptual_dataset_raw,Voicing=="Voiced"),family='binomial',adapt_delta=0.999,warmup=2000,iter=6000,chains=8)
mv_per_raw_gamm_loo<-loo(mv_per_raw_gamm,k_threshold=0.7)

View Results

Model Summary

Voiceless Data
summary(ms_per_raw_gamm,probs=my_probs,regex_pars=regex,digits=3)

Model Info:
 function:     stan_gamm4
 family:       binomial [logit]
 formula:      Code ~ BirthYear * Sex + s(BirthYear, by = Sex, k = 3, m = 2, 
       bs = "tp")
 algorithm:    sampling
 sample:       32000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 7848
 groups:       Speaker (32)

Estimates:
                           mean     sd       2.75%    50%      97.5% 
(Intercept)                 0.473   62.158 -123.727    1.576  130.864
BirthYear                  -0.001    0.031   -0.064   -0.002    0.064
SexFemale                   0.024    3.733   -7.133    0.031    7.313
BirthYear:SexFemale         0.000    0.002   -0.004    0.000    0.004
s(BirthYear):SexMale.1     -0.186    0.564   -1.526   -0.071    0.842
s(BirthYear):SexMale.2      0.066    0.403   -0.716    0.031    0.958
s(BirthYear):SexFemale.1   -0.435    0.459   -1.427   -0.366    0.267
s(BirthYear):SexFemale.2   -0.058    0.404   -0.883   -0.031    0.791

MCMC diagnostics
                         mcse  Rhat  n_eff
(Intercept)              0.627 1.001  9827
BirthYear                0.000 1.001  9826
SexFemale                0.027 1.000 19162
BirthYear:SexFemale      0.000 1.000 19182
s(BirthYear):SexMale.1   0.005 1.000 15593
s(BirthYear):SexMale.2   0.004 1.001 10293
s(BirthYear):SexFemale.1 0.005 1.001  8224
s(BirthYear):SexFemale.2 0.004 1.001  9740

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
Voiced Data
summary(mv_per_raw_gamm,probs=my_probs,regex_pars=regex,digits=3)

Model Info:
 function:     stan_gamm4
 family:       binomial [logit]
 formula:      Code ~ BirthYear * Sex + s(BirthYear, by = Sex, k = 3, m = 2, 
       bs = "tp")
 algorithm:    sampling
 sample:       32000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 12113
 groups:       Speaker (32)

Estimates:
                           mean     sd       2.75%    50%      97.5% 
(Intercept)                -3.489   65.091 -132.824   -0.730  130.248
BirthYear                   0.001    0.033   -0.064    0.000    0.070
SexFemale                   0.047    3.711   -7.117    0.052    7.330
BirthYear:SexFemale         0.000    0.002   -0.004    0.000    0.004
s(BirthYear):SexMale.1      0.044    0.395   -0.746    0.014    0.937
s(BirthYear):SexMale.2      0.117    0.405   -0.667    0.083    0.971
s(BirthYear):SexFemale.1   -0.264    0.295   -0.892   -0.224    0.211
s(BirthYear):SexFemale.2   -0.119    0.407   -0.942   -0.084    0.710

MCMC diagnostics
                         mcse  Rhat  n_eff
(Intercept)              0.719 1.001  8196
BirthYear                0.000 1.001  8195
SexFemale                0.029 1.000 16582
BirthYear:SexFemale      0.000 1.000 16602
s(BirthYear):SexMale.1   0.003 1.000 12795
s(BirthYear):SexMale.2   0.004 1.001  8416
s(BirthYear):SexFemale.1 0.004 1.001  7048
s(BirthYear):SexFemale.2 0.005 1.001  8160

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).

Nonlinear Plot

color_scheme_set(c(voiceless_col,rep("#000000",5)))
ms_per_raw_gamm_nl<-plot_nonlinear(ms_per_raw_gamm,prob=0.95)+ggtitle("Voiceless Data")
color_scheme_set(c(voiced_col,rep("#000000",5)))
mv_per_raw_gamm_nl<-plot_nonlinear(mv_per_raw_gamm,prob=0.95)+ggtitle("Voiced Data")
grid.arrange(ms_per_raw_gamm_nl,mv_per_raw_gamm_nl,ncol=1)

HDI and Bayes Factors

Voiceless Data
hdi(ms_per_raw_gamm)
Highest Density Interval 

Parameter           |           95% HDI
---------------------------------------
(Intercept)         | [-126.58, 132.76]
BirthYear           | [  -0.07,   0.06]
SexFemale           | [  -7.16,   7.45]
BirthYear:SexFemale | [  -0.00,   0.00]

# Fixed effects smooth_sd

Parameter               | Function |           95% HDI
------------------------------------------------------
s(BirthYear):SexMale1   |   smooth | [   0.00,   2.24]
s(BirthYear):SexMale2   |   smooth | [   0.00,   1.95]
s(BirthYear):SexFemale1 |   smooth | [   0.00,   2.33]
s(BirthYear):SexFemale2 |   smooth | [   0.00,   1.95]
bayesfactor(ms_per_raw_gamm)
Sampling priors, please wait...
Warning: Bayes factors might not be precise.
  For precise Bayes factors, sampling at least 40,000 posterior samples is recommended.
Bayes Factor (Savage-Dickey density ratio) 

Parameter           |    BF
---------------------------
(Intercept)         | 0.098
BirthYear           | 0.100
SexFemale           | 0.702
BirthYear:SexFemale | 0.695

* Evidence Against The Null: 0
rope(ms_per_raw_gamm,range=rope_range(ms_per_raw_gamm),ci_method='hdi',ci=0.95)
# Proportion of samples inside the ROPE [-0.18, 0.18]:

# Fixed Effects (Conditional Model)

Parameter           | inside ROPE
---------------------------------
(Intercept)         |      0.43 %
BirthYear           |    100.00 %
SexFemale           |      4.09 %
BirthYear:SexFemale |    100.00 %

# Smooth Terms

Parameter               | inside ROPE
-------------------------------------
s(BirthYear):SexMale1   |     22.61 %
s(BirthYear):SexMale2   |     29.21 %
s(BirthYear):SexFemale1 |     16.19 %
s(BirthYear):SexFemale2 |     28.46 %

Voiced Data

hdi(mv_per_raw_gamm)
Highest Density Interval 

Parameter           |           95% HDI
---------------------------------------
(Intercept)         | [-134.37, 134.05]
BirthYear           | [  -0.07,   0.07]
SexFemale           | [  -7.04,   7.53]
BirthYear:SexFemale | [  -0.00,   0.00]

# Fixed effects smooth_sd

Parameter               | Function |           95% HDI
------------------------------------------------------
s(BirthYear):SexMale1   |   smooth | [   0.00,   1.94]
s(BirthYear):SexMale2   |   smooth | [   0.00,   1.98]
s(BirthYear):SexFemale1 |   smooth | [   0.00,   1.99]
s(BirthYear):SexFemale2 |   smooth | [   0.00,   1.99]
bayesfactor(mv_per_raw_gamm)
Sampling priors, please wait...
Warning: Bayes factors might not be precise.
  For precise Bayes factors, sampling at least 40,000 posterior samples is recommended.
Bayes Factor (Savage-Dickey density ratio) 

Parameter           |    BF
---------------------------
(Intercept)         | 0.096
BirthYear           | 0.097
SexFemale           | 0.710
BirthYear:SexFemale | 0.698

* Evidence Against The Null: 0
rope(mv_per_raw_gamm,range=rope_range(mv_per_raw_gamm),ci_method='hdi',ci=0.95)
# Proportion of samples inside the ROPE [-0.18, 0.18]:

# Fixed Effects (Conditional Model)

Parameter           | inside ROPE
---------------------------------
(Intercept)         |      0.40 %
BirthYear           |    100.00 %
SexFemale           |      4.22 %
BirthYear:SexFemale |    100.00 %

# Smooth Terms

Parameter               | inside ROPE
-------------------------------------
s(BirthYear):SexMale1   |     26.54 %
s(BirthYear):SexMale2   |     26.59 %
s(BirthYear):SexFemale1 |     22.01 %
s(BirthYear):SexFemale2 |     26.28 %

Posterior Draws

color_scheme_set(c(voiceless_col,rep("#000000",5)))
ms_per_raw_post<-mcmc_areas(ms_per_raw,regex_pars=regex,prob_outer=0.95)+ggtitle("Voiceless")
color_scheme_set(c(voiced_col,rep("#000000",5)))
mv_per_raw_post<-mcmc_areas(mv_per_raw,regex_pars=regex,prob_outer=0.95)+ggtitle("Voiced")
grid.arrange(ms_per_raw_post,mv_per_raw_post,ncol=1)

Region of Practical Equivalence (ROPE)

ms_per_raw_rope<-plot(rope(ms_per_raw,range=rope_range(ms_per_raw)))+ggtitle("Voiceless")+scale_fill_manual(values=c(voiceless_col,voiceless_col_alt))
Possible multicollinearity between AgeGroupYounger:SexFemale and AgeGroupYounger (r = 0.79), Sigma[Word:SexFemale,SexFemale] and Sigma[Word:AgeGroupYounger:SexFemale,AgeGroupYounger] (r = 0.7), Sigma[Word:AgeGroupYounger:SexFemale,AgeGroupYounger:SexFemale] and Sigma[Word:SexFemale,SexFemale] (r = 0.78). This might lead to inappropriate results. See 'Details' in '?rope'.
mv_per_raw_rope<-plot(rope(mv_per_raw,range=rope_range(mv_per_raw)))+ggtitle("Voiced")+scale_fill_manual(values=c(voiced_col,voiced_col_alt))
Possible multicollinearity between AgeGroupYounger:SexFemale and AgeGroupYounger (r = 0.74). This might lead to inappropriate results. See 'Details' in '?rope'.
grid.arrange(ms_per_raw_rope,mv_per_raw_rope,ncol=1)

Shinystan Apps (will open in new browser if code is run)

Voiceless Data

launch_shinystan(ms_per_raw)

Voiced Data

launch_shinystan(mv_per_raw)
save.image('../Data/all_models_final.RData')
LS0tCnRpdGxlOiAiVGggU3RvcHBpbmcgaW4gUGhpbGFkZWxwaGlhIFB1ZXJ0byBSaWNhbiBFbmdsaXNoIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQojIFNldHVwCiMjIFNldCByYW5kb20gc2VlZAoKVGhpcyBlbnN1cmVzIHRoYXQgYWxsIHJlc3VsdHMgYXJlIHJlcGxpY2FibGUKYGBge3J9CnNldC5zZWVkKDUyNDE5ODgpCmBgYAoKIyMgTG9hZCBQYWNrYWdlcwoKRGVmaW5lIGEgZnVuY3Rpb24gdGhhdCB3aWxsIHRha2UgYSBsaXN0IG9mIHJlcXVpcmVkIHBhY2thZ2VzLCBpbnN0YWxsIGFueSB0aGF0IGFyZSBtaXNzaW5nLCBhbmQgYWRkIGFsbCBwYWNrYWdlcyB0byB0aGUgbG9jYWwgd29ya3NwYWNlOgoKYGBge3J9CmdldF9wYWNrYWdlczwtZnVuY3Rpb24ocGFja2FnZV9saXN0KXsKICAjIEluc3RhbGwgcGFja2FnZXMgbm90IHlldCBpbnN0YWxsZWQKICBpbnN0YWxsZWRfcGFja2FnZXMgPC0gcGFja2FnZV9saXN0ICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpCiAgaWYgKGFueShpbnN0YWxsZWRfcGFja2FnZXMgPT0gRkFMU0UpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2VfbGlzdFshaW5zdGFsbGVkX3BhY2thZ2VzXSkKICB9CiAgIyBQYWNrYWdlcyBsb2FkaW5nCiAgaW52aXNpYmxlKGxhcHBseShwYWNrYWdlX2xpc3QsIHJlcXVpcmUsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpfQpgYGAKCkFwcGx5IHRvIHRoZSBwYWNrYWdlIGxpc3QgZm9yIHRoaXMgYW5hbHlzaXM6CgpgYGB7ciwgcHJpbnQ9RkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPUZBTFNFfQpwYWNrYWdlczwtYygnZHBseXInLCd0aWR5cicsJ21hZ3JpdHRyJywnZ2dwbG90MicsJ3N0YXRjb21wJywnYmF5ZXNwbG90JywncnN0YW5hcm0nLCdiYXllc3Rlc3RSJywnQmF5ZXNGYWN0b3InLCdpcnInLCdncmlkRXh0cmEnLCdwYXJhbGxlbCcsJ3NoaW55c3RhbicpCmdldF9wYWNrYWdlcyhwYWNrYWdlcykKYGBgCgpTZXQgc29tZSBlbnZpcm9ubWVudGFsIHZhcmlhYmxlczoKCmBgYHtyLCByZXN1bHRzPUZBTFNFfQpvcHRpb25zKG1jLmNvcmVzPWRldGVjdENvcmVzKCkpCnRlc3RfcmVnZXg9Il5zY2FsZS4qfF5cXChJbnQuKiIKcmVnZXg9Il5cXChJbnR8XkFnZS4qfF5TZXguKnxeQmlydGguKnxec1xcKEIuKiIKbXlfcHJvYnM9YygwLjAyNzUsMC41LDAuOTc1KQpgYGAKCiMjIFNldCB1cCBXb3Jrc3BhY2UKCiMjIyBTZXQgU2Vzc2lvbiBWYXJpYWJsZXMKClNldCBjb2xvciBzY2hlbWUgYW5kIGxpbmUgdHlwZXMgZm9yIHRva2VucwoKYGBge3IsIHJlc3VsdHM9RkFMU0V9CnZvaWNlZF9jb2w8LVJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg0LCAiUGFpcmVkIilbMV0Kdm9pY2VkX2NvbF9hbHQ8LVJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg0LCAiUGFpcmVkIilbMl0Kdm9pY2VsZXNzX2NvbDwtUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDQsICJQYWlyZWQiKVszXQp2b2ljZWxlc3NfY29sX2FsdDwtUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDQsICJQYWlyZWQiKVs0XQptYWxlX2NvbDwtUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDQsICJBY2NlbnQiKVsyXQpmZW1hbGVfY29sPC1SQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoNCwgIkFjY2VudCIpWzNdCmZlbWFsZV9saW5lPC0nc29saWQnCm1hbGVfbGluZTwtJ2Rhc2hlZCcKYGBgCgojIyMgU2V0IFBsb3QgVGhlbWUKClNldCBnZW5lcmFsIHRoZW1lIGZvciBwbG90cwoKYGBge3IsIHJlc3VsdHM9RkFMU0V9CnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkrCiAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMiksCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMSwiY20iKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE1KSwKICAgICAgICAgICAgICAgICAgcGFuZWwuc3BhY2luZz11bml0KDIsImxpbmVzIiksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGNvbG9yPSdibGFjaycpLAogICAgICAgICAgICAgICAgICBzdHJpcC50ZXh0PWVsZW1lbnRfdGV4dChjb2xvcj0nYmxhY2snLHNpemU9MTgpLAogICAgICAgICAgICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KGNvbG9yPSdibGFjaycsc2l6ZT0xMiksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KGNvbG9yPSdibGFjaycsc2l6ZT0xNSkpKQpgYGAKCiMjIyBQcmUtbG9hZCBtb2RlbHMKQnkgZGVmYXVsdCwgdGhlIGNodW5rcyBpbiB0aGlzIG5vdGVib29rIHRoYXQgcGVydGFpbiB0byB0aGUgc3RhdGlzdGljYWwgYW5hbHlzaXMgaGF2ZSBiZWVuIHNldCBub3QgdG8gZXZhbHVhdGUuIFRoaXMgd2lsbCBsb2FkIGFsbCBtb2RlbHMgdG8gdGhlIHdvcmtzcGFjZSBzbyB0aGV5IGNhbiBiZSBxdWVyaWVkIHdpdGhvdXQgaGF2aW5nIHRvIHdhaXQgZm9yIHRoZW0gdG8gcnVuLgpgYGB7cn0KbG9hZCgnLi4vRGF0YS9hbGxfbW9kZWxzX2ZpbmFsLlJEYXRhJykKYGBgCgojIFRlc3QgU2V0IERhdGEKCkJ5IGRlZmF1bHQsIHdlJ2xsIGFzc3VtZSB0aGF0IHRoZSBzY3JpcHQgaGFzbid0IGJlZW4gbW92ZWQgZnJvbSBpdHMgb3JpZ2luYWwgbG9jYXRpb24gYW5kIHRoYXQgYWxsIGRhdGEgZmlsZXMgYXJlIGluIHRoZSBzYW1lIGRpcmVjdG9yeSBhcyB0aGlzIHNjcmlwdC4gSWYgdGhhdCdzIG5vdCB0cnVlLCB1c2UgYHNldHdkKClgIHRvIGNoYW5nZSB0aGUgd29ya2luZyBkaXJlY3RvcnkgZmlyc3QuCgojIyBEYXRhIFByb2Nlc3NpbmcKCkxvYWQgaW4gdGVzdCBzZXQgYW5kIHByb2Nlc3MKCmBgYHtyfQp0ZXN0X3NldD1yZWFkLmRlbGltKCcuLi9EYXRhL3Rlc3RzZXRfcmVzdWx0cy50eHQnKQpwcm9jZXNzX3Rlc3Rfc2V0PWZ1bmN0aW9uKGRhdGEpewogIGRhdGEkU3BlYWtlcj13aXRoKGRhdGEsZmFjdG9yKGlmZWxzZShzdWJzdHIoRmlsZSwxLDEpPT0nQScsIkEiLCJHIikpKQogIGRhdGEkRmlsZT1hcy5mYWN0b3IoZGF0YSRGaWxlKQogIGRhdGEkUGhvbmU9YXMuZmFjdG9yKGRhdGEkUGhvbmUpCiAgZGF0YSRXb3JkPWFzLmZhY3RvcihkYXRhJFdvcmQpCiAgZGF0YSRNZWFuX0hOUj1hcy5udW1lcmljKGRhdGEkTWVhbl9ITlIpCiAgcmV0dXJuKGRhdGEpfQp0ZXN0X3NldCAlPD4lIHByb2Nlc3NfdGVzdF9zZXQoKQpgYGAKCkRpdmlkZSBpbnRvIHZvaWNlbGVzcyBhbmQgdm9pY2VkCgpgYGB7cn0Kdm9pY2VkX3Rlc3Rfc2V0PC10ZXN0X3NldCAlPiUgZmlsdGVyKC4sUGhvbmU9PSdESCd8UGhvbmU9PSdEJykgJT4lIGRyb3BsZXZlbHMoKQp2b2ljZWxlc3NfdGVzdF9zZXQ8LXRlc3Rfc2V0ICU+JSBmaWx0ZXIoLixQaG9uZT09IlRIInxQaG9uZT09IlQiKSAlPiUgZHJvcGxldmVscygpCnZvaWNlZF90ZXN0X3NldCRQaG9uZTwtcmVsZXZlbCh2b2ljZWRfdGVzdF9zZXQkUGhvbmUscmVmPSJESCIpCnZvaWNlbGVzc190ZXN0X3NldCRQaG9uZTwtcmVsZXZlbCh2b2ljZWxlc3NfdGVzdF9zZXQkUGhvbmUscmVmPSJUSCIpCmBgYAoKIyMgRGF0YSBWaXN1YWxpemF0aW9uCgpgYGB7cn0KcGhvbmVzPC1yZXYoYygnL864LycsJy90LycsJy/DsC8nLCcvZC8nKSkKY29nPC1nZ3Bsb3QodGVzdF9zZXQsYWVzKHk9UGhvbmUseD1Db0csZmlsbD1QaG9uZSkpK2dlb21fdmlvbGluKCkrc3RhdF9zdW1tYXJ5KGdlb209J2Vycm9yYmFyJyx3aWR0aD0wLjI1LGZ1bi5kYXRhPSdtZWFuX2NsX2Jvb3QnKStzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSdQYWlyZWQnKStzY2FsZV95X2Rpc2NyZXRlKGxhYmVscz1waG9uZXMpK2Nvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwxNTAwKSkrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdub25lJykrbGFicyh4PSJDZW50ZXIgb2YgR3Jhdml0eSIpCnNrZXc8LWdncGxvdCh0ZXN0X3NldCxhZXMoeD1Ta2V3bmVzcyx5PVBob25lLGZpbGw9UGhvbmUpKStnZW9tX3Zpb2xpbigpK3N0YXRfc3VtbWFyeShnZW9tPSdlcnJvcmJhcicsd2lkdGg9MC4yNSxmdW4uZGF0YT0nbWVhbl9jbF9ib290Jykrc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0nUGFpcmVkJykrc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9cGhvbmVzKStjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjUpKSt0aGVtZShsZWdlbmQucG9zaXRpb249J25vbmUnKQprdXI8LWdncGxvdCh0ZXN0X3NldCxhZXMoeD1LdXJ0b3Npcyx5PVBob25lLGZpbGw9UGhvbmUpKStnZW9tX3Zpb2xpbigpK3N0YXRfc3VtbWFyeShnZW9tPSdlcnJvcmJhcicsd2lkdGg9MC4yNSxmdW4uZGF0YT0nbWVhbl9jbF9ib290Jykrc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0nUGFpcmVkJykrc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9cGhvbmVzKStjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsNTAwKSkrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdub25lJykKaG5yPC1nZ3Bsb3QodGVzdF9zZXQsYWVzKHg9TWVhbl9ITlIseT1QaG9uZSxmaWxsPVBob25lKSkrZ2VvbV92aW9saW4oKStzdGF0X3N1bW1hcnkoZ2VvbT0nZXJyb3JiYXInLHdpZHRoPTAuMjUsZnVuLmRhdGE9J21lYW5fY2xfYm9vdCcpK3NjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9J1BhaXJlZCcpK3NjYWxlX3lfZGlzY3JldGUobGFiZWxzPXBob25lcykrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdub25lJykrbGFicyh4PSJNZWFuIEhOUiIpCmdyaWQuYXJyYW5nZShjb2csaG5yLHNrZXcsa3VyKS0+ZmlndXJlMQpgYGAKCiMjIERhdGEgQW5hbHlzaXMKCiMjIyBSdW4gbW9kZWxzCgojIyMjIFZvaWNlbGVzcyBEYXRhCgpgYGB7ciwgcmVzdWx0cz0naGlkZScsZXZhbD1GfQptc190ZXN0c2V0PXN0YW5fZ2xtZXIoUGhvbmV+c2NhbGUoTWVhbl9ITlIpK3NjYWxlKENvRykrc2NhbGUoU2tld25lc3MpK3NjYWxlKEt1cnRvc2lzKSsoMXxTcGVha2VyKSx2b2ljZWxlc3NfdGVzdF9zZXQsZmFtaWx5PSdiaW5vbWlhbCcsYWRhcHRfZGVsdGE9MC45OSx3YXJtdXA9MjAwMCxpdGVyPTYwMDAsY2hhaW5zPTgpCm1zX3Rlc3RzZXRfbG9vPC1sb28obXNfdGVzdHNldCxrX3RocmVzaG9sZD0wLjcpCmBgYAoKIyMjIyBWb2ljZWQgRGF0YQoKYGBge3IsIHJlc3VsdHM9J2hpZGUnLGV2YWw9Rn0KbXZfdGVzdHNldD1zdGFuX2dsbWVyKFBob25lfnNjYWxlKE1lYW5fSE5SKStzY2FsZShDb0cpK3NjYWxlKFNrZXduZXNzKStzY2FsZShLdXJ0b3NpcykrKDF8U3BlYWtlciksdm9pY2VkX3Rlc3Rfc2V0LGZhbWlseT0nYmlub21pYWwnLGFkYXB0X2RlbHRhPTAuOTksd2FybXVwPTIwMDAsaXRlcj02MDAwLGNoYWlucz04KQptdl90ZXN0c2V0X2xvbzwtbG9vKG12X3Rlc3RzZXQsa190aHJlc2hvbGQ9MC43KQpgYGAKCiMjIyBWaWV3IFJlc3VsdHMKCiMjIyMgTW9kZWwgU3VtbWFyeQoKYGBge3J9CnN1bW1hcnkobXNfdGVzdHNldCxyZWdleF9wYXJzPXRlc3RfcmVnZXgscHJvYnM9bXlfcHJvYnMsZGlnaXRzPTMpCmBgYAoKYGBge3J9CnN1bW1hcnkobXZfdGVzdHNldCxyZWdleF9wYXJzPXRlc3RfcmVnZXgscHJvYnM9bXlfcHJvYnMsZGlnaXRzPTMpCmBgYAojIyMjIEhESXMgYW5kIEJheWVzIEZhY3RvcnMKCmBgYHtyfQpwcmludCgiVm9pY2VsZXNzIERhdGE6IikKaGRpKG1zX3Rlc3RzZXQpCmJheWVzZmFjdG9yKG1zX3Rlc3RzZXQpCmBgYApgYGB7cn0KcHJpbnQoIlZvaWNlZCBEYXRhOiIpCmhkaShtdl90ZXN0c2V0KQpiYXllc2ZhY3Rvcihtdl90ZXN0c2V0KQpgYGAKCgoKIyMjIyBQb3N0ZXJpb3IgRHJhd3MKCmBgYHtyfQpjb2xvcl9zY2hlbWVfc2V0KGModm9pY2VsZXNzX2NvbCxyZXAoIiMwMDAwMDAiLDUpKSkKbXNfdGVzdHNldF9wb3N0PC1tY21jX2FyZWFzKG1zX3Rlc3RzZXQscmVnZXhfcGFycz10ZXN0X3JlZ2V4LHByb2Jfb3V0ZXI9MC45NSkrZ2d0aXRsZSgiVm9pY2VsZXNzIikrc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHM9YygiSW50ZXJjZXB0IiwiTWVhbiBITlIiLCJDb0ciLCJTa2V3bmVzcyIsIkt1cnRvc2lzIikpK2dlb21fdmxpbmUoeGludGVyY2VwdD0wLGxpbmV0eXBlPSdkb3R0ZWQnKStsYWJzKHg9IlN0b3AgTGlrZWxpaG9vZCIpCmNvbG9yX3NjaGVtZV9zZXQoYyh2b2ljZWRfY29sLHJlcCgiIzAwMDAwMCIsNSkpKQptdl90ZXN0c2V0X3Bvc3Q8LW1jbWNfYXJlYXMobXZfdGVzdHNldCxyZWdleF9wYXJzPXRlc3RfcmVnZXgscHJvYl9vdXRlcj0wLjk1KStnZ3RpdGxlKCJWb2ljZWQiKStzY2FsZV95X2Rpc2NyZXRlKGxhYmVscz1jKCJJbnRlcmNlcHQiLCJNZWFuIEhOUiIsIkNvRyIsIlNrZXduZXNzIiwiS3VydG9zaXMiKSkrZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAsbGluZXR5cGU9J2RvdHRlZCcpK2xhYnMoeD0iU3RvcCBMaWtlbGlob29kIikKZ3JpZC5hcnJhbmdlKG1zX3Rlc3RzZXRfcG9zdCxtdl90ZXN0c2V0X3Bvc3QsbmNvbD0yKS0+ZmlndXJlMgpgYGAKCiMjIyMgUmVnaW9uIG9mIFByYWN0aWNhbCBFcXVpdmFsZW5jZSAoUk9QRSkKCmBgYHtyLHdhcm5pbmc9RkFMU0V9Cm1zX3Rlc3RzZXRfcm9wZTwtcGxvdChyb3BlKG1zX3Rlc3RzZXQscmFuZ2U9cm9wZV9yYW5nZShtc190ZXN0c2V0KSxjaV9tZXRob2Q9J2hkaScpLGFscGhhPTAuNzUpK2dndGl0bGUoIlZvaWNlbGVzcyIpK3NjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKHZvaWNlbGVzc19jb2wsdm9pY2VsZXNzX2NvbF9hbHQpKQptdl90ZXN0c2V0X3JvcGU8LXBsb3Qocm9wZShtdl90ZXN0c2V0LHJhbmdlPXJvcGVfcmFuZ2UobXZfdGVzdHNldCksY2lfbWV0aG9kPSdoZGknKSxhbHBoYT0wLjc1KStnZ3RpdGxlKCJWb2ljZWQiKStzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Yyh2b2ljZWRfY29sLHZvaWNlZF9jb2xfYWx0KSkKZ3JpZC5hcnJhbmdlKG1zX3Rlc3RzZXRfcm9wZSxtdl90ZXN0c2V0X3JvcGUsbmNvbD0xKQpgYGAKCiMjIyMgU2hpbnlzdGFuIEFwcCAod2lsbCBvcGVuIGluIG5ldyBicm93c2VyIGlmIGNvZGUgaXMgcnVuKQoKYGBge3IsIGV2YWw9RkFMU0V9CmxhdW5jaF9zaGlueXN0YW4obXNfdGVzdHNldCkKYGBgCgpgYGB7cixldmFsPUZBTFNFfQpsYXVuY2hfc2hpbnlzdGFuKG12X3Rlc3RzZXQpCmBgYAoKIyBQUkVQIERhdGEKClBSRVAgPSBQdWVydG8gUmljYW4gRW5nbGlzaCBpbiBQaGlsYWRlbHBoaWEgQ29ycHVzCgpbTW9yZSBpbmZvXShodHRwczovL29zZi5pby83a200ci8pCgojIyBEYXRhIFByb2Nlc3NpbmcKCiMjIyBMb2FkIERhdGEgZnJvbSBTb3VyY2VzCgpCeSBkZWZhdWx0LCB3ZSdsbCBhc3N1bWUgdGhhdCB0aGUgc2NyaXB0IGhhc24ndCBiZWVuIG1vdmVkIGZyb20gaXRzIG9yaWdpbmFsIGxvY2F0aW9uIGFuZCB0aGF0IGFsbCBkYXRhIGZpbGVzIGFyZSBpbiB0aGUgc2FtZSBkaXJlY3RvcnkgYXMgdGhpcyBzY3JpcHQuIElmIHRoYXQncyBub3QgdHJ1ZSwgdXNlIGBzZXR3ZCgpYCB0byBjaGFuZ2UgdGhlIHdvcmtpbmcgZGlyZWN0b3J5IGZpcnN0LgoKYGBge3J9CnByZXBfZGF0YT1yZWFkLmRlbGltKCcuLi9EYXRhL3ByZXBfZGF0YS50eHQnKQpzb2Npb19kYXRhPXJlYWQuY3N2KCcuLi9EYXRhL3ByZXBfc29jaW8uY3N2JykKcGVyY2VwdHVhbF9kYXRhPXJlYWQuZGVsaW0oJy4uL0RhdGEvcGVyY2VwdHVhbF9kYXRhLnR4dCcpCnBlcmNlcHR1YWxfdG9rZW5zX3ZvaWNpbmdfY29kZXM9cmVhZC5kZWxpbSgnLi4vRGF0YS92b2ljaW5nX2NvZGVzLnR4dCcpCmBgYAoKIyMjIFByb2Nlc3MgRGF0YQoKU3RhcnQgYnkgZ2V0dGluZyBwaG9uZXRpYyBjb250ZXh0CgpgYGB7cn0KcHJlcF9kYXRhJEZvbGxvd2luZ1N0cmVzcz1hcy5mYWN0b3Iod2l0aChwcmVwX2RhdGEsc3Vic3RyKEZvbGxvd2luZ1Bob25lLG5jaGFyKEZvbGxvd2luZ1Bob25lKSxuY2hhcihGb2xsb3dpbmdQaG9uZSkpKSkKcHJlcF9kYXRhJEZvbGxvd2luZ1Zvd2VsPWFzLmZhY3Rvcih3aXRoKHByZXBfZGF0YSxzdWJzdHIoRm9sbG93aW5nUGhvbmUsMSxuY2hhcihGb2xsb3dpbmdQaG9uZSktMSkpKQpwcmVwX2RhdGEkRm9sbG93aW5nUGhvbmU9YXMuZmFjdG9yKHByZXBfZGF0YSRGb2xsb3dpbmdQaG9uZSkKYGBgCgpSZW1vdmUgdG9rZW5zIHRoYXQgYXJlbid0IHByZXZvY2FsaWMgKGkuZS4sIGFyZSBjb25zb25hbnQgY2x1c3RlcnMpCgpgYGB7cn0KcHJlcF9kYXRhICU8PiUgZmlsdGVyKCEoRm9sbG93aW5nUGhvbmUgJWluJSBsaXN0KCdSJywnVycsJ1knKSkpIApwcmVwX2RhdGEgJTw+JSBtdXRhdGUoVm9pY2luZz1pZl9lbHNlKFBob25lICVpbiUgbGlzdCgnRCcsJ0RIJyksJ1ZvaWNlZCcsJ1ZvaWNlbGVzcycpLE1hbm5lcj1pZl9lbHNlKFBob25lICVpbiUgbGlzdCgnRCcsJ1QnKSwnU3RvcCcsJ0ZyaWNhdGl2ZScpLE1ldHJpYz1pZl9lbHNlKFZvaWNpbmc9PSdWb2ljZWQnLCdNZWFuX0hOUicsJ1NrZXduZXNzJykpCmBgYAoKSm9pbiB0byBzb2Npb2RlbW9ncmFwaGljIGRhdGEgYW5kIHNwZWNpZnkgZGF0YSBzdHJ1Y3R1cmUKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CnByZXBfZGF0YSAlPiUgbGVmdF9qb2luKC4sc29jaW9fZGF0YSwgYnk9YygnRmlsZSc9J1NwZWFrZXInKSktPnByZXBfc29jaW8gIyBNZXJnZSBzb2NpbyBkYXRhIHdpdGggc3BlYWtlciBkYXRhCnByZXBfc29jaW8kU2V4PWFzLmZhY3RvcihwcmVwX3NvY2lvJFNleCkKcHJlcF9zb2NpbyRNZWFuX0hOUj1hcy5udW1lcmljKHByZXBfc29jaW8kTWVhbl9ITlIpCnByZXBfc29jaW8kRmlsZT1hcy5mYWN0b3IocHJlcF9zb2NpbyRGaWxlKQpwcmVwX3NvY2lvJFdvcmQ9Z3N1YigiXFwoXFwoIiwiIixwcmVwX3NvY2lvJFdvcmQpCnByZXBfc29jaW8kV29yZD1nc3ViKCJcXClcXCkiLCIiLHByZXBfc29jaW8kV29yZCkKcHJlcF9zb2NpbyRXb3JkPWFzLmZhY3RvcihwcmVwX3NvY2lvJFdvcmQpCnByZXBfc29jaW8kUGhvbmU9YXMuZmFjdG9yKHByZXBfc29jaW8kUGhvbmUpCnByZXBfc29jaW8gJTw+JSBtdXRhdGUoQWdlR3JvdXA9aWZfZWxzZShCaXJ0aFllYXI+PTE5ODUsJ1lvdW5nZXInLCdPbGRlcicpLFNwZWFrZXI9RmlsZSkKYGBgCgpHZXQgYXZlcmFnZSB2YWx1ZXMgYnkgc3BlYWtlciwgdm9pY2luZywgYW5kIHBob25ldGljIGNvbnRleHQKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CnByZXBfZGF0YSRQcmVjZWRpbmdQaG9uZT1hcy5mYWN0b3IocHJlcF9kYXRhJFByZWNlZGluZ1Bob25lKQpwcmVwX2RhdGEkUHJlY2VkaW5nV29yZD1hcy5mYWN0b3IocHJlcF9kYXRhJFByZWNlZGluZ1dvcmQpCnByZXBfZGF0YSRTa2V3bmVzcz1hcy5udW1lcmljKHByZXBfZGF0YSRTa2V3bmVzcykKcHJlcF9kYXRhJE1lYW5fSE5SPWFzLm51bWVyaWMocHJlcF9kYXRhJE1lYW5fSE5SKQoKc3BlYWtlcl9hdmdzPC1wcmVwX2RhdGEgJT4lIGZpbHRlcihQaG9uZT09J0QnIHwgUGhvbmU9PSdUJykgJT4lIAogIGdyb3VwX2J5KEZpbGUsIFBob25lLCBGb2xsb3dpbmdWb3dlbCwgRm9sbG93aW5nU3RyZXNzKSAlPiUgCiAgcmVmcmFtZShjb3VudD1uKCksSE5SX3N0b3A9bWVhbihuYS5vbWl0KE1lYW5fSE5SKSksc2RfSE5SX3N0b3A9c2QobmEub21pdChNZWFuX0hOUikpLCBza2V3bmVzc19zdG9wPW1lYW4obmEub21pdChTa2V3bmVzcykpLHNkX3NrZXduZXNzX3N0b3A9c2QobmEub21pdChTa2V3bmVzcykpKSAlPiUKICB1bmdyb3VwKCkKCnZvaWNlZF9hdmdzPC1zcGVha2VyX2F2Z3MgJT4lIGZpbHRlcihQaG9uZT09J0QnKSAlPiUgc2VsZWN0KC1QaG9uZSkKdm9pY2VsZXNzX2F2Z3M8LXNwZWFrZXJfYXZncyAlPiUgZmlsdGVyKFBob25lPT0nVCcpICU+JSBzZWxlY3QoLVBob25lKQpgYGAKCkZ1bmN0aW9ucyB0byBjYWxjdWxhdGUgbm9ybWFsaXplZCBzdG9wcGluZyByYXRpb3MgYW5kIHJlbW92ZSBvdXRsaWVycyBcPjMgc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIHRoZSBncm91cCBtZWFuLCBieSBzcGVha2VyLgoKYGBge3J9Cm1ha2Vfc3RvcHBpbmdfcmF0aW9zPC1mdW5jdGlvbihkYXRhLHApewogIGRhdGEgJTw+JSBmaWx0ZXIoUGhvbmU9PXApICU+JSAKICBsZWZ0X2pvaW4odm9pY2VkX2F2Z3MsYnk9YygnRmlsZScsJ0ZvbGxvd2luZ1Zvd2VsJywnRm9sbG93aW5nU3RyZXNzJykpICU+JSAKICBtdXRhdGUoSE5SX3JhdGlvPU1lYW5fSE5SL0hOUl9zdG9wLHNrZXduZXNzX3JhdGlvPVNrZXduZXNzL3NrZXduZXNzX3N0b3ApICU+JQogIG11dGF0ZShyYXRpbz0oaWZfZWxzZShQaG9uZT09J1RIJyxza2V3bmVzc19yYXRpbyxpZl9lbHNlKFBob25lPT0iREgiLEhOUl9yYXRpbyxOQSkpKSkgJT4lCiAgbGVmdF9qb2luKHNvY2lvX2RhdGEsYnk9YygnRmlsZSc9J1NwZWFrZXInKSkgJT4lIHJlbmFtZShTcGVha2VyPUZpbGUpICU+JQogIG11dGF0ZShBZ2VHcm91cD1hcy5mYWN0b3IoaWZlbHNlKEJpcnRoWWVhcj49MTk4NSwnWW91bmdlcicsJ09sZGVyJykpKQogIHJldHVybihkYXRhKQp9CgpyZW1vdmVfb3V0bGllcnM8LWZ1bmN0aW9uKGRhdGEpewogIGRhdGEgJT4lIGZpbHRlcighaXMubmEocmF0aW8pKSAlPiUgbXV0YXRlKFBzZXVkb255bT1pZmVsc2UoLiRTcGVha2VyPT0nUzM5JywnQ2FyaW5hJywuJFBzZXVkb255bSkpJT4lCiAgICBtdXRhdGUobWVhbl9yYXRpb19ieXNwZWFrZXI9bWVhbihyYXRpbyksc2RfcmF0aW9fYnlzcGVha2VyPXNkKHJhdGlvKSkgJT4lCiAgICB1bmdyb3VwKCkgJT4lCiAgICBmaWx0ZXIoYWJzKHJhdGlvLW1lYW5fcmF0aW9fYnlzcGVha2VyKTw9MypzZF9yYXRpb19ieXNwZWFrZXIpICU+JQogICAgZmlsdGVyKGFicyhyYXRpby1tZWFuKHJhdGlvKSk8PTMqc2QocmF0aW8pKS0+cmVzdWx0CiAgcHJpbnQocGFzdGUwKCJSZW1vdmVkICIsbnJvdyhkYXRhKS1ucm93KHJlc3VsdCksIiByb3dzLCBvciAiLHJvdW5kKDEwMCooMS1ucm93KHJlc3VsdCkvbnJvdyhkYXRhKSksMiksIiUgb2YgZGF0YS4iKSkKICByZXR1cm4ocmVzdWx0KQp9CmBgYAoKQXBwbHkgdG8gZGF0YSBhbmQgY3JlYXRlIHByb2R1Y3Rpb24gZGF0YXNldAoKYGBge3Isd2FybmluZz1GQUxTRX0Kdm9pY2VsZXNzPC1tYWtlX3N0b3BwaW5nX3JhdGlvcyhwcmVwX2RhdGEsIlRIIikgJT4lIHJlbW92ZV9vdXRsaWVycygpCnZvaWNlZDwtbWFrZV9zdG9wcGluZ19yYXRpb3MocHJlcF9kYXRhLCJESCIpICU+JSByZW1vdmVfb3V0bGllcnMoKQpwcm9kdWN0aW9uX2RhdGE8LXJiaW5kKHZvaWNlZCx2b2ljZWxlc3MpCmBgYAoKIyMgRGF0YSBWaXN1YWxpemF0aW9uCgojIyMgRGF0YSBkaXN0cmlidXRpb24KCmBgYHtyLCBlY2hvPVQsZmlnLmtlZXA9J2FsbCd9CnByb2R1Y3Rpb25fZGF0YSAlPiUgZ3JvdXBfYnkoV29yZCxQaG9uZSkgJT4lIHN1bW1hcmlzZShjb3VudD1uKCkpICU+JSBhcnJhbmdlKGRlc2MoY291bnQpKSAlPiUgIHsuLT4+IHdvcmRfY3RzfSAlPiUKICBnZ3Bsb3QoLixhZXMoeD1jb3VudCkpK2dlb21fZGVuc2l0eSgpK3NjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsNTAwLGJ5PTUwKSkrY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDUwMCkpK2dndGl0bGUoIldvcmQgRGlzdHJpYnV0aW9uIikKCmBgYAoKTm90ZSB0aGF0IG9ubHkgMiBvZiB0aGUgdG9wIDEwIHdvcmRzIGFyZSAvzrgvIHdvcmRzICh0aGluayx0aGluZ3MpCgpgYGB7cn0Kd29yZF9jdHMgJT4lCiAgZmlsdGVyKGNvdW50Pj00MCYoUGhvbmU9PSdESCd8UGhvbmU9PSdUSCcpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lIAogIGhlYWQoMjApIApgYGAKClRvcCAxMCAvw7AvIHdvcmRzCmBgYHtyfQp3b3JkX2N0cyAlPiUKICBmaWx0ZXIoUGhvbmU9PSdESCcpICU+JSAKICBhcnJhbmdlKGRlc2MoY291bnQpKSAlPiUgCiAgaGVhZCgxMCkgCmBgYAoKVG9wIDEwIC/OuC8gd29yZHMKYGBge3J9CndvcmRfY3RzICU+JQogIGZpbHRlcihQaG9uZT09J1RIJykgJT4lIAogIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JSAKICBoZWFkKDEwKSAKYGBgCgoKVmlzdWFsaXplIGRpc3RyaWJ1dGlvbiBvZiBmcmljYXRpdmVzIGFuZCBzdG9wcyBhY3Jvc3MgZGF0YSBieSBtZXRyaWMKCmBgYHtyLGZpZy5rZWVwPSdhbGwnfQoKZ2dwbG90KHByZXBfc29jaW8sYWVzKHg9aWZfZWxzZShWb2ljaW5nPT0nVm9pY2VkJyxNZWFuX0hOUixTa2V3bmVzcyksZmlsbD1NYW5uZXIpKSsKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41KStmYWNldF9ncmlkKFZvaWNpbmd+LikrCiAgbGFicyh4PSJNZXRyaWMiLHk9IkRlbnNpdHkiKStzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb249J2NpdmlkaXMnLGJlZ2luPTAuMyxlbmQ9MC45KSt0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScpCgpnZ3Bsb3QocHJlcF9zb2NpbyxhZXMoeD1pZl9lbHNlKFZvaWNpbmc9PSdWb2ljZWQnLE1lYW5fSE5SLFNrZXduZXNzKSxmaWxsPU1hbm5lcikpKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjUpK2ZhY2V0X2dyaWQoVm9pY2luZ35TZXgpKwogIGxhYnMoeD0iTWV0cmljIix5PSJEZW5zaXR5Iikrc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uPSd2aXJpZGlzJyxiZWdpbj0wLjIsZW5kPTAuNikrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKQogCmdncGxvdChwcmVwX3NvY2lvLGFlcyh4PWlmX2Vsc2UoVm9pY2luZz09J1ZvaWNlZCcsTWVhbl9ITlIsU2tld25lc3MpLGZpbGw9TWFubmVyKSkrCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkrZmFjZXRfZ3JpZChTZXh+QWdlR3JvdXArVm9pY2luZykrCiAgbGFicyh4PSJNZXRyaWMiLHk9IkRlbnNpdHkiKStzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb249J2luZmVybm8nKSt0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScpCgpgYGAKCkluZGl2aWR1YWwgcmFuZ2VzIHRvIHNlZSBpZiB0aGVyZSBhcmUgYW55IG1ham9yIG91dGxpZXJzIHRoYXQgcmVtYWluIGFmdGVyIHRyaW1taW5nIHByb2NlZHVyZQoKYGBge3IsZmlnLmtlZXA9J2FsbCd9CmdncGxvdCh2b2ljZWxlc3MsYWVzKHg9UHNldWRvbnltLHk9cmF0aW8pKSsKICBnZW9tX2JveHBsb3QoZmlsbD12b2ljZWxlc3NfY29sKSsKICBsYWJzKHg9IlNwZWFrZXIiLHk9Ik5vcm1hbGl6ZWQgU3RvcHBpbmcgUmF0aW9cbihTa2V3bmVzcykiKSsKICBnZ3RpdGxlKCJWb2ljZWxlc3MiKStzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgtMSw1LGJ5PTAuNSkpK2dlb21faGxpbmUoeWludGVyY2VwdD0xLGxpbmV0eXBlPSdkYXNoZWQnKSsKICBjb29yZF9mbGlwKCktPnBfdm9pY2VsZXNzX2J5c3BlYWtlcgpnZ3Bsb3Qodm9pY2VkLGFlcyh4PVBzZXVkb255bSx5PXJhdGlvKSkrCiAgZ2VvbV9ib3hwbG90KGZpbGw9dm9pY2VkX2NvbCkrCiAgbGFicyh4PSJTcGVha2VyIix5PSJOb3JtYWxpemVkIFN0b3BwaW5nIFJhdGlvXG4oTWVhbiBITlIpIikrCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoLTEsNSxieT0xKSkrCiAgZ2d0aXRsZSgiVm9pY2VkIikrZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEsbGluZXR5cGU9J2Rhc2hlZCcpKwogIGNvb3JkX2ZsaXAoKS0+cF92b2ljZWRfYnlzcGVha2VyCmdyaWQuYXJyYW5nZShwX3ZvaWNlbGVzc19ieXNwZWFrZXIscF92b2ljZWRfYnlzcGVha2VyLG5jb2w9MikKYGBgCgojIyMgQmFzaWMgU29jaW9saW5ndWlzdGljIFRyZW5kcwoKVmlldyBnZW5lcmFsIHRyZW5kcyBieSBiaXJ0aCB5ZWFyCgpgYGB7cn0KYWdlX3ZvaWNlbGVzczwtZ2dwbG90KHZvaWNlbGVzcyxhZXMoeD1CaXJ0aFllYXIseT1za2V3bmVzc19yYXRpby0xLGxpbmV0eXBlPVNleCkpKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLGxpbmV3aWR0aD0wLjUsbGluZXR5cGU9J2RvdHRlZCcpK2dlb21fcnVnKHNpZGVzPSJiciIsbGluZXR5cGU9J3NvbGlkJykrCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwPVNleCksY29sb3I9J2JsYWNrJyxmaWxsPXZvaWNlbGVzc19jb2wsbWV0aG9kPSdnYW0nLGZvcm11bGE9eX5zKHgsaz0zLG09MikpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKG1hbGVfY29sLGZlbWFsZV9jb2wpKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgtMS41LDEuNSxieT0wLjUpKStnZW9tX3BvaW50KGFscGhhPTAuMDUscG9zaXRpb249cG9zaXRpb25faml0dGVyKDAuMykpKwogIGxhYnMoeD0iQmlydGggWWVhciIseT0iU2tld25lc3MgUmF0aW9cbihEZXZpYW5jZSBmcm9tIDEpIikrY29vcmRfY2FydGVzaWFuKHlsaW09YygtMS41LDEuNSkpKwogIGdndGl0bGUoIlZvaWNlbGVzcyIpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJyxzdHJpcC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE1KSkrc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMC4xKSkKCmFnZV92b2ljZWQ8LWdncGxvdCh2b2ljZWQsYWVzKHg9QmlydGhZZWFyLHk9SE5SX3JhdGlvLTEsbGluZXR5cGU9U2V4KSkrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsbGluZXdpZHRoPTAuNSxsaW5ldHlwZT0nZG90dGVkJykrZ2VvbV9ydWcoc2lkZXM9ImJyIixsaW5ldHlwZT0nc29saWQnKSsKICBnZW9tX3Ntb290aChhZXMoZ3JvdXA9U2V4KSxjb2xvcj0nYmxhY2snLGZpbGw9dm9pY2VkX2NvbCxtZXRob2Q9J2dhbScsZm9ybXVsYT15fnMoeCxrPTMsbT0yKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoLTEuNSwxLjUsYnk9MC41KSkrZ2VvbV9wb2ludChhbHBoYT0wLjA1LHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcigwLjMpKSsKICBsYWJzKHg9IkJpcnRoIFllYXIiLHk9IkhOUiBSYXRpb1xuKERldmlhbmNlIGZyb20gMSkiKSsKICBnZ3RpdGxlKCJWb2ljZWQiKStjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKC0xLjUsMS41KSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLHN0cmlwLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTUpKStzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLjEpKQoKZ3JpZC5hcnJhbmdlKGFnZV92b2ljZWxlc3MsYWdlX3ZvaWNlZCxuY29sPTIpLT5maWd1cmU0CnByaW50KGZpZ3VyZTQpCmBgYAoKVmlldyB0cmVuZHMgYnkgYmlydGggeWVhciwgZ3JvdXBpbmcgYnkgc3BlYWtlcgoKYGBge3J9CmFnZV92b2ljZWRfYnlzcGVha2VyPC1nZ3Bsb3Qodm9pY2VkLGFlcyh4PUJpcnRoWWVhcix5PUhOUl9yYXRpbykpKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xLGxpbmV3aWR0aD0wLjUsbGluZXR5cGU9J2RvdHRlZCcpKwogIGdlb21fYm94cGxvdChhZXMoZ3JvdXA9U3BlYWtlciksZmlsbD12b2ljZWRfY29sKSsKICBsYWJzKHg9IkJpcnRoIFllYXIiLHk9Ik5vcm1hbGl6ZWQgSE5SIFJhdGlvIikrZ2d0aXRsZSgiVm9pY2VkIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKStzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLjEpKSsKICBmYWNldF93cmFwKH5TZXgsbmNvbD0xKQoKYWdlX3ZvaWNlbGVzc19ieXNwZWFrZXI8LWdncGxvdCh2b2ljZWxlc3MsYWVzKHg9QmlydGhZZWFyLHk9c2tld25lc3NfcmF0aW8pKSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MSxsaW5ld2lkdGg9MC41LGxpbmV0eXBlPSdkb3R0ZWQnKSsKICBnZW9tX2JveHBsb3QoYWVzKGdyb3VwPVNwZWFrZXIpLGZpbGw9dm9pY2VsZXNzX2NvbCkrCiAgbGFicyh4PSJCaXJ0aCBZZWFyIix5PSJOb3JtYWxpemVkIFNrZXduZXNzIFJhdGlvIikrZ2d0aXRsZSgiVm9pY2VsZXNzIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLjEpKSsKICBmYWNldF93cmFwKH5TZXgsbmNvbD0xKQoKZ3JpZC5hcnJhbmdlKGFnZV92b2ljZWxlc3NfYnlzcGVha2VyLGFnZV92b2ljZWRfYnlzcGVha2VyLG5jb2w9MikKYGBgCgojIyBEYXRhIEFuYWx5c2lzCgpTZXQgQ29udHJhc3RzCgpgYGB7cn0Kc2V0X2NvbnRyYXN0czwtZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSRTZXg8LWFzLmZhY3RvcihkYXRhJFNleCkKICBkYXRhJFNleDwtcmVsZXZlbChkYXRhJFNleCxyZWY9J01hbGUnKQogIGRhdGEkQWdlR3JvdXA9YXMuZmFjdG9yKGRhdGEkQWdlR3JvdXApCiAgZGF0YSRBZ2VHcm91cDwtcmVsZXZlbChkYXRhJEFnZUdyb3VwLHJlZj0iT2xkZXIiKQogIHJldHVybihkYXRhKQogIH0KYGBgCgpgYGB7cn0Kdm9pY2VkICU8PiUgc2V0X2NvbnRyYXN0cygpCnZvaWNlbGVzcyAlPD4lIHNldF9jb250cmFzdHMoKQp2b2ljZWQkSE5SX3JhdGlvX2RldmlhbmNlPXZvaWNlZCRITlJfcmF0aW8tMQp2b2ljZWxlc3Mkc2tld25lc3NfcmF0aW9fZGV2aWFuY2U9dm9pY2VsZXNzJHNrZXduZXNzX3JhdGlvLTEKYGBgCgojIyMgUnVuIE1vZGVscwoKIyMjIyBWb2ljZWxlc3MgRGF0YQpgYGB7cixldmFsPUZ9Cm1zX3ByZXBfZ2FtbTwtc3Rhbl9nYW1tNChza2V3bmVzc19yYXRpb19kZXZpYW5jZX5TZXgqQmlydGhZZWFyK3MoQmlydGhZZWFyLGJzPSd0cCcsYnk9U2V4LGs9MyxtPTIpLHJhbmRvbT1+KDF8U3BlYWtlciksZGF0YT12b2ljZWxlc3MsYWRhcHRfZGVsdGE9MC45OTksd2FybXVwPTIwMDAsaXRlcj02MDAwLGNoYWlucz04KQptc19wcmVwX2dhbW1fbG9vPC1sb28obXNfcHJlcF9nYW1tLGtfdGhyZXNob2xkPTAuNykKYGBgCgoKIyMjIyBWb2ljZWQgRGF0YQpgYGB7cixldmFsPUZ9Cm12X3ByZXBfZ2FtbTwtc3Rhbl9nYW1tNChITlJfcmF0aW9fZGV2aWFuY2V+U2V4KkJpcnRoWWVhcitzKEJpcnRoWWVhcixicz0ndHAnLGJ5PVNleCxrPTMsbT0yKSxyYW5kb209figxfFNwZWFrZXIpLGRhdGE9dm9pY2VkLGFkYXB0X2RlbHRhPTAuOTk5LHdhcm11cD0yMDAwLGl0ZXI9NjAwMCxjaGFpbnM9OCkKbXZfcHJlcF9nYW1tX2xvbzwtbG9vKG12X3ByZXBfZ2FtbSxrX3RocmVzaG9sZD0wLjcpCmBgYAoKIyMjIFZpZXcgUmVzdWx0cwoKCiMjIyMgTW9kZWwgU3VtbWFyeQoKIyMjIyMgVm9pY2VsZXNzCgpgYGB7cn0Kc3VtbWFyeShtc19wcmVwX2dhbW0scmVnZXhfcGFycz1yZWdleCxwcm9icz1teV9wcm9icyxkaWdpdHM9MykKYGBgCgojIyMjIyBWb2ljZWQKCmBgYHtyfQpzdW1tYXJ5KG12X3ByZXBfZ2FtbSxyZWdleF9wYXJzPXJlZ2V4LHByb2JzPW15X3Byb2JzLGRpZ2l0cz0zKQoKYGBgCgojIyMjIyBIRElzIGFuZCBCYXllcyBGYWN0b3JzCgojIyMjIyBWb2ljZWxlc3MKYGBge3J9CmhkaShtc19wcmVwX2dhbW0pCmJheWVzZmFjdG9yKG1zX3ByZXBfZ2FtbSkKYGBgCiMjIyMjIFZvaWNlZApgYGB7cn0KaGRpKG12X3ByZXBfZ2FtbSkKYmF5ZXNmYWN0b3IobXZfcHJlcF9nYW1tKQpgYGAKCiMjIyMjIFBvc3RlcmlvciBEcmF3cwoKYGBge3IsZmlnLmtlZXA9J2FsbCd9CmNvbG9yX3NjaGVtZV9zZXQoYyh2b2ljZWxlc3NfY29sLHJlcCgiIzAwMDAwMCIsNSkpKQptc19wcmVwX2dhbW1fcG9zdDwtbWNtY19hcmVhcyhtc19wcmVwX2dhbW0scmVnZXhfcGFycz1yZWdleCxwcm9iX291dGVyPTAuOTUpK2dndGl0bGUoIlZvaWNlbGVzcyIpCmNvbG9yX3NjaGVtZV9zZXQoYyh2b2ljZWRfY29sLHJlcCgiIzAwMDAwMCIsNSkpKQptdl9wcmVwX2dhbW1fcG9zdDwtbWNtY19hcmVhcyhtdl9wcmVwX2dhbW0scmVnZXhfcGFycz1yZWdleCxwcm9iX291dGVyPTAuOTUpK2dndGl0bGUoIlZvaWNlZCIpCmdyaWQuYXJyYW5nZShtc19wcmVwX2dhbW1fcG9zdCxtdl9wcmVwX2dhbW1fcG9zdCxuY29sPTIpCmBgYAoKTm9ubGluZWFyIFBsb3QKCmBgYHtyIGZpZy5rZWVwPSdhbGwnfQpjb2xvcl9zY2hlbWVfc2V0KGModm9pY2VsZXNzX2NvbCxyZXAoIiMwMDAwMDAiLDUpKSkKbXNfcHJlcF9nYW1tX25sPC1wbG90X25vbmxpbmVhcihtc19wcmVwX2dhbW0scHJvYj0wLjk1KStnZ3RpdGxlKCJWb2ljZWxlc3MgRGF0YSIpCmNvbG9yX3NjaGVtZV9zZXQoYyh2b2ljZWRfY29sLHJlcCgiIzAwMDAwMCIsNSkpKQptdl9wcmVwX2dhbW1fbmw8LXBsb3Rfbm9ubGluZWFyKG12X3ByZXBfZ2FtbSxwcm9iPTAuOTUpK2dndGl0bGUoIlZvaWNlZCBEYXRhIikKZ3JpZC5hcnJhbmdlKG1zX3ByZXBfZ2FtbV9ubCxtdl9wcmVwX2dhbW1fbmwsbmNvbD0xKQpgYGAKCiMjIyMjIFJlZ2lvbiBvZiBQcmFjdGljYWwgRXF1aXZhbGVuY2UgKFJPUEUpCiMjIyMjIFZvaWNlbGVzcwpgYGB7cn0KbXNfcHJlcF9nYW1tX3JvcGU8LXBsb3Qocm9wZShtc19wcmVwX2dhbW0scmFuZ2U9cm9wZV9yYW5nZShtc19wcmVwX2dhbW0pLGNpX21ldGhvZD0naGRpJykpK2dndGl0bGUoIlZvaWNlbGVzcyIpK3NjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKHZvaWNlbGVzc19jb2wsdm9pY2VsZXNzX2NvbF9hbHQpKSt0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScpCm1zX3ByZXBfZ2FtbV9yb3BlCmBgYAojIyMjIyBWb2ljZWQKYGBge3J9Cm12X3ByZXBfZ2FtbV9yb3BlPC1wbG90KHJvcGUobXZfcHJlcF9nYW1tLHJhbmdlPXJvcGVfcmFuZ2UobXZfcHJlcF9nYW1tKSxjaV9tZXRob2Q9J2hkaScpKStnZ3RpdGxlKCJWb2ljZWQiKStzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Yyh2b2ljZWRfY29sLHZvaWNlZF9jb2xfYWx0KSkrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKQoKbXZfcHJlcF9nYW1tX3JvcGUKYGBgCgojIyMjIyBTaGlueXN0YW4gQXBwcyAod2lsbCBvcGVuIGluIG5ldyBicm93c2VyIGlmIGNvZGUgaXMgcnVuKQoKVm9pY2VsZXNzIERhdGEKCmBgYHtyLCBldmFsPUZBTFNFfQpsYXVuY2hfc2hpbnlzdGFuKG1zX3ByZXBfZ2FtbSkKYGBgCgpWb2ljZWQgRGF0YQoKYGBge3IsIGV2YWw9RkFMU0V9CmxhdW5jaF9zaGlueXN0YW4obXZfcHJlcF9nYW1tKQpgYGAKCiMgUGVyY2VwdHVhbCBDb2RpbmcKCiMjIERhdGEgUHJvY2Vzc2luZwoKYGBge3J9CnBlcmNlcHR1YWxfZGF0YSAlPiUgZmlsdGVyKENvZGVyIT0iIikgJT4lCiAgbXV0YXRlKHRva2VuPXBhc3RlKFNwZWFrZXIsV29yZCxTdGFydCxFbmQsc2VwPSJfIikpICU+JSAKICBsZWZ0X2pvaW4oLixwZXJjZXB0dWFsX3Rva2Vuc192b2ljaW5nX2NvZGVzLGJ5PSdXb3JkJykgJT4lIAogIGxlZnRfam9pbiguLHNvY2lvX2RhdGEsYnk9J1NwZWFrZXInKSAlPiUgCiAgbXV0YXRlKEFnZUdyb3VwPWlmZWxzZShhcy5udW1lcmljKEJpcnRoWWVhcik+PTE5ODUsJ1lvdW5nZXInLCdPbGRlcicpKSAlPiUKICBmaWx0ZXIoQ29kZSE9IlVuc3VyZSIpLT5wZXJjZXB0dWFsX2RhdGFzZXRfcmF3CnBlcmNlcHR1YWxfZGF0YXNldF9yYXcgJTw+JSBzZXRfY29udHJhc3RzKCkKYGBgCgpgYGB7cn0KcGVyY2VwdHVhbF9kYXRhc2V0X3JhdyAlPiUgCiAgZ3JvdXBfYnkoU3BlYWtlcixBZ2VHcm91cCxCaXJ0aFllYXIsU2V4LFZvaWNpbmcsQ29kZSkgJT4lIAogIHRhbGx5KCkgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209Q29kZSx2YWx1ZXNfZnJvbT1uKSAlPiUgCiAgbXV0YXRlKFRvdGFsPVN0b3ArRnJpY2F0aXZlLFN0b3BwaW5nX1JhdGU9MTAwKlN0b3AvKFRvdGFsKSkgJT4lIAogIHsuLT4+IHBlcmNlcHR1YWxfcmF0ZXNfcmF3fSAlPiUgcHJpbnQoKQpgYGAKCiMjIyBMYWJvdiAoZGgpIEluZGV4IENvbXBhcmlzb24KYGBge3J9CnBlcmNlcHR1YWxfZGF0YSAlPiUgZmlsdGVyKENvZGVyIT0iIikgJT4lCiAgbXV0YXRlKHRva2VuPXBhc3RlKFNwZWFrZXIsV29yZCxTdGFydCxFbmQsc2VwPSJfIikpICU+JSAKICBsZWZ0X2pvaW4oLixwZXJjZXB0dWFsX3Rva2Vuc192b2ljaW5nX2NvZGVzLGJ5PSdXb3JkJykgJT4lIAogIGxlZnRfam9pbiguLHNvY2lvX2RhdGEsYnk9J1NwZWFrZXInKSAlPiUgCiAgbXV0YXRlKEFnZUdyb3VwPWlmZWxzZShhcy5udW1lcmljKEJpcnRoWWVhcik+PTE5ODUsJ1lvdW5nZXInLCdPbGRlcicpLGRoX3ZhbHVlPWNhc2VfbWF0Y2goQ29kZSwiVW5zdXJlIn4xLCJGcmljYXRpdmUifjAsIlN0b3AifjIpKSAlPiUgey4tPj5wZXJjZXB0dWFsX2dyb3VwX2xldmVsfSAlPiUKICBncm91cF9ieShBZ2VHcm91cCxTZXgpICU+JSByZWZyYW1lKGRoX2luZGV4PTEwMCptZWFuKGRoX3ZhbHVlKSkKCnBlcmNlcHR1YWxfZ3JvdXBfbGV2ZWwgJT4lIAogIGdyb3VwX2J5KFZvaWNpbmcsQWdlR3JvdXAsU2V4LENvZGUpICU+JSAKICBmaWx0ZXIoQ29kZSE9IlVuc3VyZSIpICU+JQogIHRhbGx5KCkgJT4lIGdyb3VwX2J5KEFnZUdyb3VwLFNleCxWb2ljaW5nKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT1Db2RlLHZhbHVlc19mcm9tPW4pICU+JSAKICBtdXRhdGUoVG90YWw9U3RvcCtGcmljYXRpdmUsU3RvcHBpbmdfUmF0ZT0xMDAqU3RvcC8oVG90YWwpKQoKcGVyY2VwdHVhbF9ncm91cF9sZXZlbCAlPiUgZmlsdGVyKENvZGVyIT0iIikgJT4lIGdyb3VwX2J5KFNleCxBZ2VHcm91cCkgJT4lIHJlZnJhbWUoY291bnQ9bl9kaXN0aW5jdChTcGVha2VyKSkKYGBgCgojIyMgUmF0ZXIgQ29uc2Vuc3VzCgpHZXQgRmxlaXNzJyBLYXBwYSBmb3IgSVJSCgpgYGB7cn0KcGVyY2VwdHVhbF9kYXRhICU+JSBmaWx0ZXIoQ29kZXIhPSIiKSAlPiUgCiAgbXV0YXRlKHRva2VuPXBhc3RlKFNwZWFrZXIsV29yZCxTdGFydCxFbmQsc2VwPSJfIikpICU+JSAKICBzZWxlY3QodG9rZW4sQ29kZXIsQ29kZSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209YENvZGVyYCx2YWx1ZXNfZnJvbT1Db2RlKSAlPiUgey4gLT4+IHBlcmNlcHR1YWxfaXJyfSAlPiUKICBrYXBwYW0uZmxlaXNzKCkKYGBgCgpTaG93IHRva2VucyBieSBsZXZlbCBvZiBhZ3JlZW1lbnQKYGBge3J9CnBlcmNlcHR1YWxfaXJyICU+JSByb3d3aXNlICU+JQogICAgICAgIG11dGF0ZShkaXN0aW5jdCA9IG5fZGlzdGluY3QodW5saXN0KGFjcm9zcyhzdGFydHNfd2l0aCgiQSIpKSkpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgLT4gcGVyY2VwdHVhbF9pcnJfYWdyZWVtZW50CgpwZXJjZXB0dWFsX2lycl9hZ3JlZW1lbnQKYGBgCiMjIERhdGEgVmlzdWFsaXphdGlvbgoKU3RvcHBpbmcgUmF0ZXMgYnkgU2V4IGFuZCBCaXJ0aCBZZWFyIGJhc2VkIG9uIEluZGl2aWR1YWwgUmF0ZXIgU2NvcmUKCmBgYHtyfQpwZXJjZXB0dWFsX3ZvaWNlbGVzc19yYXRlc19yYXc8LSBwZXJjZXB0dWFsX3JhdGVzX3JhdyAlPiUKICBmaWx0ZXIoVm9pY2luZz09IlZvaWNlbGVzcyIpICU+JQogIGdncGxvdChhZXMoeD1CaXJ0aFllYXIseT1TdG9wcGluZ19SYXRlLGxpbmV0eXBlPVNleCkpKwogIGdlb21fcnVnKHNpZGVzPSJiciIsbGluZXR5cGU9J3NvbGlkJykrCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwPVNleCksY29sb3I9J2JsYWNrJyxmaWxsPXZvaWNlbGVzc19jb2wsbWV0aG9kPSdnYW0nLGZvcm11bGE9eX5zKHgsaz0zLG09MikpKwogICNzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgtMS41LDEuNSxieT0wLjUpKSsKICBnZW9tX3BvaW50KGFscGhhPTAuNSxwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIoMC4zKSkrCiAgbGFicyh4PSJCaXJ0aCBZZWFyIix5PSJTdG9wcGluZyBSYXRlXG4oUGVyY2VwdHVhbCwgYnkgUmF0ZXIpIikrCiAgZ2d0aXRsZSgiVm9pY2VsZXNzIikrc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKG1hbGVfbGluZSxmZW1hbGVfbGluZSkpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJyxzdHJpcC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE1KSkrc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gMC4xKSkKCnBlcmNlcHR1YWxfdm9pY2VkX3JhdGVzX3JhdzwtIHBlcmNlcHR1YWxfcmF0ZXNfcmF3ICU+JQogIGZpbHRlcihWb2ljaW5nPT0iVm9pY2VkIikgJT4lCiAgZ2dwbG90KGFlcyh4PUJpcnRoWWVhcix5PVN0b3BwaW5nX1JhdGUsbGluZXR5cGU9U2V4KSkrCiAgZ2VvbV9ydWcoc2lkZXM9ImJyIixsaW5ldHlwZT0nc29saWQnKSsKICBnZW9tX3Ntb290aChhZXMoZ3JvdXA9U2V4KSxjb2xvcj0nYmxhY2snLGZpbGw9dm9pY2VkX2NvbCxtZXRob2Q9J2dhbScsZm9ybXVsYT15fnMoeCxrPTMsbT0yKSkrCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUscG9zaXRpb249cG9zaXRpb25faml0dGVyKDAuMywwLjMpKSsKICBsYWJzKHg9IkJpcnRoIFllYXIiLHk9IlN0b3BwaW5nIFJhdGVcbihQZXJjZXB0dWFsLCBieSBSYXRlcikiKSsKICBnZ3RpdGxlKCJWb2ljZWQiKStzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMobWFsZV9saW5lLGZlbWFsZV9saW5lKSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLHN0cmlwLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTUpKStzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSAwLjEpKQoKZ3JpZC5hcnJhbmdlKHBlcmNlcHR1YWxfdm9pY2VsZXNzX3JhdGVzX3JhdyxwZXJjZXB0dWFsX3ZvaWNlZF9yYXRlc19yYXcsbmNvbD0yKQpgYGAKCgojIyBEYXRhIEFuYWx5c2lzCgojIyMgUnVuIE1vZGVscwoKRm9yY2Ugb3V0Y29tZSB2YXJpYWJsZSB0byBiZSBmYWN0b3IgZm9yIGJpbm9taWFsIGdhbQoKYGBge3J9CnBlcmNlcHR1YWxfZGF0YXNldF9yYXckQ29kZSAlPD4lIGFzLmZhY3RvcigpCmBgYAoKIyMjIyBWb2ljZWxlc3MgRGF0YQoKYGBge3IsZXZhbD1GfQptc19wZXJfcmF3X2dhbW08LXN0YW5fZ2FtbTQoQ29kZX5CaXJ0aFllYXIqU2V4K3MoQmlydGhZZWFyLGJ5PVNleCxrPTMsbT0yLGJzPSd0cCcpLHJhbmRvbT1+KDF8U3BlYWtlciksZGF0YT1zdWJzZXQocGVyY2VwdHVhbF9kYXRhc2V0X3JhdyxWb2ljaW5nPT0iVm9pY2VsZXNzIiksZmFtaWx5PSdiaW5vbWlhbCcsYWRhcHRfZGVsdGE9MC45OTksd2FybXVwPTIwMDAsaXRlcj02MDAwLGNoYWlucz04KQoKbXNfcGVyX3Jhd19nYW1tX2xvbzwtbG9vKG1zX3Blcl9yYXdfZ2FtbSkKYGBgCgojIyMjIFZvaWNlZCBEYXRhCgpgYGB7cixldmFsPUZBTFNFfQptdl9wZXJfcmF3X2dhbW08LXN0YW5fZ2FtbTQoQ29kZX5CaXJ0aFllYXIqU2V4K3MoQmlydGhZZWFyLGJ5PVNleCxrPTMsbT0yLGJzPSd0cCcpLHJhbmRvbT1+KDF8U3BlYWtlciksZGF0YT1zdWJzZXQocGVyY2VwdHVhbF9kYXRhc2V0X3JhdyxWb2ljaW5nPT0iVm9pY2VkIiksZmFtaWx5PSdiaW5vbWlhbCcsYWRhcHRfZGVsdGE9MC45OTksd2FybXVwPTIwMDAsaXRlcj02MDAwLGNoYWlucz04KQptdl9wZXJfcmF3X2dhbW1fbG9vPC1sb28obXZfcGVyX3Jhd19nYW1tLGtfdGhyZXNob2xkPTAuNykKYGBgCgoKIyMjIFZpZXcgUmVzdWx0cwoKIyMjIyBNb2RlbCBTdW1tYXJ5CgojIyMjIyBWb2ljZWxlc3MgRGF0YQoKYGBge3J9CnN1bW1hcnkobXNfcGVyX3Jhd19nYW1tLHByb2JzPW15X3Byb2JzLHJlZ2V4X3BhcnM9cmVnZXgsZGlnaXRzPTMpCmBgYAoKIyMjIyMgVm9pY2VkIERhdGEKCmBgYHtyfQpzdW1tYXJ5KG12X3Blcl9yYXdfZ2FtbSxwcm9icz1teV9wcm9icyxyZWdleF9wYXJzPXJlZ2V4LGRpZ2l0cz0zKQpgYGAKCiMjIyMgTm9ubGluZWFyIFBsb3QKCmBgYHtyIGZpZy5rZWVwPSdhbGwnfQpjb2xvcl9zY2hlbWVfc2V0KGModm9pY2VsZXNzX2NvbCxyZXAoIiMwMDAwMDAiLDUpKSkKbXNfcGVyX3Jhd19nYW1tX25sPC1wbG90X25vbmxpbmVhcihtc19wZXJfcmF3X2dhbW0scHJvYj0wLjk1KStnZ3RpdGxlKCJWb2ljZWxlc3MgRGF0YSIpCmNvbG9yX3NjaGVtZV9zZXQoYyh2b2ljZWRfY29sLHJlcCgiIzAwMDAwMCIsNSkpKQptdl9wZXJfcmF3X2dhbW1fbmw8LXBsb3Rfbm9ubGluZWFyKG12X3Blcl9yYXdfZ2FtbSxwcm9iPTAuOTUpK2dndGl0bGUoIlZvaWNlZCBEYXRhIikKZ3JpZC5hcnJhbmdlKG1zX3Blcl9yYXdfZ2FtbV9ubCxtdl9wZXJfcmF3X2dhbW1fbmwsbmNvbD0xKQpgYGAKCiMjIyMgSERJIGFuZCBCYXllcyBGYWN0b3JzCgojIyMjIyBWb2ljZWxlc3MgRGF0YQpgYGB7cn0KaGRpKG1zX3Blcl9yYXdfZ2FtbSkKYmF5ZXNmYWN0b3IobXNfcGVyX3Jhd19nYW1tKQpyb3BlKG1zX3Blcl9yYXdfZ2FtbSxyYW5nZT1yb3BlX3JhbmdlKG1zX3Blcl9yYXdfZ2FtbSksY2lfbWV0aG9kPSdoZGknLGNpPTAuOTUpCmBgYAoKIyMjIyBWb2ljZWQgRGF0YQpgYGB7cn0KaGRpKG12X3Blcl9yYXdfZ2FtbSkKYmF5ZXNmYWN0b3IobXZfcGVyX3Jhd19nYW1tKQpyb3BlKG12X3Blcl9yYXdfZ2FtbSxyYW5nZT1yb3BlX3JhbmdlKG12X3Blcl9yYXdfZ2FtbSksY2lfbWV0aG9kPSdoZGknLGNpPTAuOTUpCmBgYAojIyMjIFBvc3RlcmlvciBEcmF3cwoKYGBge3J9CmNvbG9yX3NjaGVtZV9zZXQoYyh2b2ljZWxlc3NfY29sLHJlcCgiIzAwMDAwMCIsNSkpKQptc19wZXJfcmF3X3Bvc3Q8LW1jbWNfYXJlYXMobXNfcGVyX3JhdyxyZWdleF9wYXJzPXJlZ2V4LHByb2Jfb3V0ZXI9MC45NSkrZ2d0aXRsZSgiVm9pY2VsZXNzIikKY29sb3Jfc2NoZW1lX3NldChjKHZvaWNlZF9jb2wscmVwKCIjMDAwMDAwIiw1KSkpCm12X3Blcl9yYXdfcG9zdDwtbWNtY19hcmVhcyhtdl9wZXJfcmF3LHJlZ2V4X3BhcnM9cmVnZXgscHJvYl9vdXRlcj0wLjk1KStnZ3RpdGxlKCJWb2ljZWQiKQpncmlkLmFycmFuZ2UobXNfcGVyX3Jhd19wb3N0LG12X3Blcl9yYXdfcG9zdCxuY29sPTEpCmBgYAoKIyMjIyBSZWdpb24gb2YgUHJhY3RpY2FsIEVxdWl2YWxlbmNlIChST1BFKQoKYGBge3J9Cm1zX3Blcl9yYXdfcm9wZTwtcGxvdChyb3BlKG1zX3Blcl9yYXcscmFuZ2U9cm9wZV9yYW5nZShtc19wZXJfcmF3KSkpK2dndGl0bGUoIlZvaWNlbGVzcyIpK3NjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKHZvaWNlbGVzc19jb2wsdm9pY2VsZXNzX2NvbF9hbHQpKQptdl9wZXJfcmF3X3JvcGU8LXBsb3Qocm9wZShtdl9wZXJfcmF3LHJhbmdlPXJvcGVfcmFuZ2UobXZfcGVyX3JhdykpKStnZ3RpdGxlKCJWb2ljZWQiKStzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Yyh2b2ljZWRfY29sLHZvaWNlZF9jb2xfYWx0KSkKZ3JpZC5hcnJhbmdlKG1zX3Blcl9yYXdfcm9wZSxtdl9wZXJfcmF3X3JvcGUsbmNvbD0xKQpgYGAKCiMjIyMjIFNoaW55c3RhbiBBcHBzICh3aWxsIG9wZW4gaW4gbmV3IGJyb3dzZXIgaWYgY29kZSBpcyBydW4pCgpWb2ljZWxlc3MgRGF0YQoKYGBge3IsZXZhbD1GfQpsYXVuY2hfc2hpbnlzdGFuKG1zX3Blcl9yYXcpCmBgYAoKVm9pY2VkIERhdGEKCmBgYHtyLGV2YWw9Rn0KbGF1bmNoX3NoaW55c3Rhbihtdl9wZXJfcmF3KQpgYGAKCgpgYGB7cn0KI3NhdmUuaW1hZ2UoJy4uL0RhdGEvYWxsX21vZGVsc19maW5hbC5SRGF0YScpCmBgYAo=